xref: /openbmc/qemu/scripts/qapi/schema.py (revision bf83f04e13063bb723fb8b9df789a3613c6d0ceb)
1# -*- coding: utf-8 -*-
2#
3# QAPI schema internal representation
4#
5# Copyright (c) 2015-2019 Red Hat Inc.
6#
7# Authors:
8#  Markus Armbruster <armbru@redhat.com>
9#  Eric Blake <eblake@redhat.com>
10#  Marc-André Lureau <marcandre.lureau@redhat.com>
11#
12# This work is licensed under the terms of the GNU GPL, version 2.
13# See the COPYING file in the top-level directory.
14
15# TODO catching name collisions in generated code would be nice
16
17import os
18import re
19from collections import OrderedDict
20
21from qapi.common import c_name, pointer_suffix
22from qapi.error import QAPIError, QAPIParseError, QAPISemError
23from qapi.expr import check_exprs
24from qapi.parser import QAPISchemaParser
25
26
27class QAPISchemaEntity(object):
28    meta = None
29
30    def __init__(self, name, info, doc, ifcond=None):
31        assert name is None or isinstance(name, str)
32        self.name = name
33        self._module = None
34        # For explicitly defined entities, info points to the (explicit)
35        # definition.  For builtins (and their arrays), info is None.
36        # For implicitly defined entities, info points to a place that
37        # triggered the implicit definition (there may be more than one
38        # such place).
39        self.info = info
40        self.doc = doc
41        self._ifcond = ifcond or []
42        self._checked = False
43
44    def c_name(self):
45        return c_name(self.name)
46
47    def check(self, schema):
48        assert not self._checked
49        if self.info:
50            self._module = os.path.relpath(self.info.fname,
51                                           os.path.dirname(schema.fname))
52        self._checked = True
53
54    def connect_doc(self, doc=None):
55        pass
56
57    def check_doc(self):
58        pass
59
60    @property
61    def ifcond(self):
62        assert self._checked
63        return self._ifcond
64
65    @property
66    def module(self):
67        assert self._checked
68        return self._module
69
70    def is_implicit(self):
71        return not self.info
72
73    def visit(self, visitor):
74        assert self._checked
75
76    def describe(self):
77        assert self.meta
78        return "%s '%s'" % (self.meta, self.name)
79
80
81class QAPISchemaVisitor(object):
82    def visit_begin(self, schema):
83        pass
84
85    def visit_end(self):
86        pass
87
88    def visit_module(self, fname):
89        pass
90
91    def visit_needed(self, entity):
92        # Default to visiting everything
93        return True
94
95    def visit_include(self, fname, info):
96        pass
97
98    def visit_builtin_type(self, name, info, json_type):
99        pass
100
101    def visit_enum_type(self, name, info, ifcond, members, prefix):
102        pass
103
104    def visit_array_type(self, name, info, ifcond, element_type):
105        pass
106
107    def visit_object_type(self, name, info, ifcond, base, members, variants,
108                          features):
109        pass
110
111    def visit_object_type_flat(self, name, info, ifcond, members, variants,
112                               features):
113        pass
114
115    def visit_alternate_type(self, name, info, ifcond, variants):
116        pass
117
118    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
119                      success_response, boxed, allow_oob, allow_preconfig,
120                      features):
121        pass
122
123    def visit_event(self, name, info, ifcond, arg_type, boxed):
124        pass
125
126
127class QAPISchemaInclude(QAPISchemaEntity):
128
129    def __init__(self, fname, info):
130        QAPISchemaEntity.__init__(self, None, info, None)
131        self.fname = fname
132
133    def visit(self, visitor):
134        QAPISchemaEntity.visit(self, visitor)
135        visitor.visit_include(self.fname, self.info)
136
137
138class QAPISchemaType(QAPISchemaEntity):
139    # Return the C type for common use.
140    # For the types we commonly box, this is a pointer type.
141    def c_type(self):
142        pass
143
144    # Return the C type to be used in a parameter list.
145    def c_param_type(self):
146        return self.c_type()
147
148    # Return the C type to be used where we suppress boxing.
149    def c_unboxed_type(self):
150        return self.c_type()
151
152    def json_type(self):
153        pass
154
155    def alternate_qtype(self):
156        json2qtype = {
157            'null':    'QTYPE_QNULL',
158            'string':  'QTYPE_QSTRING',
159            'number':  'QTYPE_QNUM',
160            'int':     'QTYPE_QNUM',
161            'boolean': 'QTYPE_QBOOL',
162            'object':  'QTYPE_QDICT'
163        }
164        return json2qtype.get(self.json_type())
165
166    def doc_type(self):
167        if self.is_implicit():
168            return None
169        return self.name
170
171    def describe(self):
172        assert self.meta
173        return "%s type '%s'" % (self.meta, self.name)
174
175
176class QAPISchemaBuiltinType(QAPISchemaType):
177    meta = 'built-in'
178
179    def __init__(self, name, json_type, c_type):
180        QAPISchemaType.__init__(self, name, None, None)
181        assert not c_type or isinstance(c_type, str)
182        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
183                             'value')
184        self._json_type_name = json_type
185        self._c_type_name = c_type
186
187    def c_name(self):
188        return self.name
189
190    def c_type(self):
191        return self._c_type_name
192
193    def c_param_type(self):
194        if self.name == 'str':
195            return 'const ' + self._c_type_name
196        return self._c_type_name
197
198    def json_type(self):
199        return self._json_type_name
200
201    def doc_type(self):
202        return self.json_type()
203
204    def visit(self, visitor):
205        QAPISchemaType.visit(self, visitor)
206        visitor.visit_builtin_type(self.name, self.info, self.json_type())
207
208
209class QAPISchemaEnumType(QAPISchemaType):
210    meta = 'enum'
211
212    def __init__(self, name, info, doc, ifcond, members, prefix):
213        QAPISchemaType.__init__(self, name, info, doc, ifcond)
214        for m in members:
215            assert isinstance(m, QAPISchemaEnumMember)
216            m.set_defined_in(name)
217        assert prefix is None or isinstance(prefix, str)
218        self.members = members
219        self.prefix = prefix
220
221    def check(self, schema):
222        QAPISchemaType.check(self, schema)
223        seen = {}
224        for m in self.members:
225            m.check_clash(self.info, seen)
226
227    def connect_doc(self, doc=None):
228        doc = doc or self.doc
229        if doc:
230            for m in self.members:
231                doc.connect_member(m)
232
233    def check_doc(self):
234        if self.doc:
235            self.doc.check()
236
237    def is_implicit(self):
238        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
239        return self.name.endswith('Kind') or self.name == 'QType'
240
241    def c_type(self):
242        return c_name(self.name)
243
244    def member_names(self):
245        return [m.name for m in self.members]
246
247    def json_type(self):
248        return 'string'
249
250    def visit(self, visitor):
251        QAPISchemaType.visit(self, visitor)
252        visitor.visit_enum_type(self.name, self.info, self.ifcond,
253                                self.members, self.prefix)
254
255
256class QAPISchemaArrayType(QAPISchemaType):
257    meta = 'array'
258
259    def __init__(self, name, info, element_type):
260        QAPISchemaType.__init__(self, name, info, None, None)
261        assert isinstance(element_type, str)
262        self._element_type_name = element_type
263        self.element_type = None
264
265    def check(self, schema):
266        QAPISchemaType.check(self, schema)
267        self.element_type = schema.resolve_type(
268            self._element_type_name, self.info,
269            self.info and self.info.defn_meta)
270        assert not isinstance(self.element_type, QAPISchemaArrayType)
271
272    @property
273    def ifcond(self):
274        assert self._checked
275        return self.element_type.ifcond
276
277    @property
278    def module(self):
279        assert self._checked
280        return self.element_type.module
281
282    def is_implicit(self):
283        return True
284
285    def c_type(self):
286        return c_name(self.name) + pointer_suffix
287
288    def json_type(self):
289        return 'array'
290
291    def doc_type(self):
292        elt_doc_type = self.element_type.doc_type()
293        if not elt_doc_type:
294            return None
295        return 'array of ' + elt_doc_type
296
297    def visit(self, visitor):
298        QAPISchemaType.visit(self, visitor)
299        visitor.visit_array_type(self.name, self.info, self.ifcond,
300                                 self.element_type)
301
302    def describe(self):
303        assert self.meta
304        return "%s type ['%s']" % (self.meta, self._element_type_name)
305
306
307class QAPISchemaObjectType(QAPISchemaType):
308    def __init__(self, name, info, doc, ifcond,
309                 base, local_members, variants, features):
310        # struct has local_members, optional base, and no variants
311        # flat union has base, variants, and no local_members
312        # simple union has local_members, variants, and no base
313        QAPISchemaType.__init__(self, name, info, doc, ifcond)
314        self.meta = 'union' if variants else 'struct'
315        assert base is None or isinstance(base, str)
316        for m in local_members:
317            assert isinstance(m, QAPISchemaObjectTypeMember)
318            m.set_defined_in(name)
319        if variants is not None:
320            assert isinstance(variants, QAPISchemaObjectTypeVariants)
321            variants.set_defined_in(name)
322        for f in features:
323            assert isinstance(f, QAPISchemaFeature)
324            f.set_defined_in(name)
325        self._base_name = base
326        self.base = None
327        self.local_members = local_members
328        self.variants = variants
329        self.members = None
330        self.features = features
331
332    def check(self, schema):
333        # This calls another type T's .check() exactly when the C
334        # struct emitted by gen_object() contains that T's C struct
335        # (pointers don't count).
336        if self.members is not None:
337            # A previous .check() completed: nothing to do
338            return
339        if self._checked:
340            # Recursed: C struct contains itself
341            raise QAPISemError(self.info,
342                               "object %s contains itself" % self.name)
343
344        QAPISchemaType.check(self, schema)
345        assert self._checked and self.members is None
346
347        seen = OrderedDict()
348        if self._base_name:
349            self.base = schema.resolve_type(self._base_name, self.info,
350                                            "'base'")
351            if (not isinstance(self.base, QAPISchemaObjectType)
352                    or self.base.variants):
353                raise QAPISemError(
354                    self.info,
355                    "'base' requires a struct type, %s isn't"
356                    % self.base.describe())
357            self.base.check(schema)
358            self.base.check_clash(self.info, seen)
359        for m in self.local_members:
360            m.check(schema)
361            m.check_clash(self.info, seen)
362        members = seen.values()
363
364        if self.variants:
365            self.variants.check(schema, seen)
366            self.variants.check_clash(self.info, seen)
367
368        # Features are in a name space separate from members
369        seen = {}
370        for f in self.features:
371            f.check_clash(self.info, seen)
372
373        self.members = members  # mark completed
374
375    # Check that the members of this type do not cause duplicate JSON members,
376    # and update seen to track the members seen so far. Report any errors
377    # on behalf of info, which is not necessarily self.info
378    def check_clash(self, info, seen):
379        assert self._checked
380        assert not self.variants       # not implemented
381        for m in self.members:
382            m.check_clash(info, seen)
383
384    def connect_doc(self, doc=None):
385        doc = doc or self.doc
386        if doc:
387            if self.base and self.base.is_implicit():
388                self.base.connect_doc(doc)
389            for m in self.local_members:
390                doc.connect_member(m)
391
392    def check_doc(self):
393        if self.doc:
394            self.doc.check()
395
396    @property
397    def ifcond(self):
398        assert self._checked
399        if isinstance(self._ifcond, QAPISchemaType):
400            # Simple union wrapper type inherits from wrapped type;
401            # see _make_implicit_object_type()
402            return self._ifcond.ifcond
403        return self._ifcond
404
405    def is_implicit(self):
406        # See QAPISchema._make_implicit_object_type(), as well as
407        # _def_predefineds()
408        return self.name.startswith('q_')
409
410    def is_empty(self):
411        assert self.members is not None
412        return not self.members and not self.variants
413
414    def c_name(self):
415        assert self.name != 'q_empty'
416        return QAPISchemaType.c_name(self)
417
418    def c_type(self):
419        assert not self.is_implicit()
420        return c_name(self.name) + pointer_suffix
421
422    def c_unboxed_type(self):
423        return c_name(self.name)
424
425    def json_type(self):
426        return 'object'
427
428    def visit(self, visitor):
429        QAPISchemaType.visit(self, visitor)
430        visitor.visit_object_type(self.name, self.info, self.ifcond,
431                                  self.base, self.local_members, self.variants,
432                                  self.features)
433        visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
434                                       self.members, self.variants,
435                                       self.features)
436
437
438class QAPISchemaMember(object):
439    """ Represents object members, enum members and features """
440    role = 'member'
441
442    def __init__(self, name, info, ifcond=None):
443        assert isinstance(name, str)
444        self.name = name
445        self.info = info
446        self.ifcond = ifcond or []
447        self.defined_in = None
448
449    def set_defined_in(self, name):
450        assert not self.defined_in
451        self.defined_in = name
452
453    def check_clash(self, info, seen):
454        cname = c_name(self.name)
455        if cname in seen:
456            raise QAPISemError(
457                info,
458                "%s collides with %s"
459                % (self.describe(info), seen[cname].describe(info)))
460        seen[cname] = self
461
462    def describe(self, info):
463        role = self.role
464        defined_in = self.defined_in
465        assert defined_in
466
467        if defined_in.startswith('q_obj_'):
468            # See QAPISchema._make_implicit_object_type() - reverse the
469            # mapping there to create a nice human-readable description
470            defined_in = defined_in[6:]
471            if defined_in.endswith('-arg'):
472                # Implicit type created for a command's dict 'data'
473                assert role == 'member'
474                role = 'parameter'
475            elif defined_in.endswith('-base'):
476                # Implicit type created for a flat union's dict 'base'
477                role = 'base ' + role
478            else:
479                # Implicit type created for a simple union's branch
480                assert defined_in.endswith('-wrapper')
481                # Unreachable and not implemented
482                assert False
483        elif defined_in.endswith('Kind'):
484            # See QAPISchema._make_implicit_enum_type()
485            # Implicit enum created for simple union's branches
486            assert role == 'value'
487            role = 'branch'
488        elif defined_in != info.defn_name:
489            return "%s '%s' of type '%s'" % (role, self.name, defined_in)
490        return "%s '%s'" % (role, self.name)
491
492
493class QAPISchemaEnumMember(QAPISchemaMember):
494    role = 'value'
495
496
497class QAPISchemaFeature(QAPISchemaMember):
498    role = 'feature'
499
500
501class QAPISchemaObjectTypeMember(QAPISchemaMember):
502    def __init__(self, name, info, typ, optional, ifcond=None):
503        QAPISchemaMember.__init__(self, name, info, ifcond)
504        assert isinstance(typ, str)
505        assert isinstance(optional, bool)
506        self._type_name = typ
507        self.type = None
508        self.optional = optional
509
510    def check(self, schema):
511        assert self.defined_in
512        self.type = schema.resolve_type(self._type_name, self.info,
513                                        self.describe)
514
515
516class QAPISchemaObjectTypeVariants(object):
517    def __init__(self, tag_name, info, tag_member, variants):
518        # Flat unions pass tag_name but not tag_member.
519        # Simple unions and alternates pass tag_member but not tag_name.
520        # After check(), tag_member is always set, and tag_name remains
521        # a reliable witness of being used by a flat union.
522        assert bool(tag_member) != bool(tag_name)
523        assert (isinstance(tag_name, str) or
524                isinstance(tag_member, QAPISchemaObjectTypeMember))
525        for v in variants:
526            assert isinstance(v, QAPISchemaObjectTypeVariant)
527        self._tag_name = tag_name
528        self.info = info
529        self.tag_member = tag_member
530        self.variants = variants
531
532    def set_defined_in(self, name):
533        for v in self.variants:
534            v.set_defined_in(name)
535
536    def check(self, schema, seen):
537        if not self.tag_member: # flat union
538            self.tag_member = seen.get(c_name(self._tag_name))
539            base = "'base'"
540            # Pointing to the base type when not implicit would be
541            # nice, but we don't know it here
542            if not self.tag_member or self._tag_name != self.tag_member.name:
543                raise QAPISemError(
544                    self.info,
545                    "discriminator '%s' is not a member of %s"
546                    % (self._tag_name, base))
547            # Here we do:
548            base_type = schema.lookup_type(self.tag_member.defined_in)
549            assert base_type
550            if not base_type.is_implicit():
551                base = "base type '%s'" % self.tag_member.defined_in
552            if not isinstance(self.tag_member.type, QAPISchemaEnumType):
553                raise QAPISemError(
554                    self.info,
555                    "discriminator member '%s' of %s must be of enum type"
556                    % (self._tag_name, base))
557            if self.tag_member.optional:
558                raise QAPISemError(
559                    self.info,
560                    "discriminator member '%s' of %s must not be optional"
561                    % (self._tag_name, base))
562            if self.tag_member.ifcond:
563                raise QAPISemError(
564                    self.info,
565                    "discriminator member '%s' of %s must not be conditional"
566                    % (self._tag_name, base))
567        else:                   # simple union
568            assert isinstance(self.tag_member.type, QAPISchemaEnumType)
569            assert not self.tag_member.optional
570            assert self.tag_member.ifcond == []
571        if self._tag_name:    # flat union
572            # branches that are not explicitly covered get an empty type
573            cases = set([v.name for v in self.variants])
574            for m in self.tag_member.type.members:
575                if m.name not in cases:
576                    v = QAPISchemaObjectTypeVariant(m.name, self.info,
577                                                    'q_empty', m.ifcond)
578                    v.set_defined_in(self.tag_member.defined_in)
579                    self.variants.append(v)
580        if not self.variants:
581            raise QAPISemError(self.info, "union has no branches")
582        for v in self.variants:
583            v.check(schema)
584            # Union names must match enum values; alternate names are
585            # checked separately. Use 'seen' to tell the two apart.
586            if seen:
587                if v.name not in self.tag_member.type.member_names():
588                    raise QAPISemError(
589                        self.info,
590                        "branch '%s' is not a value of %s"
591                        % (v.name, self.tag_member.type.describe()))
592                if (not isinstance(v.type, QAPISchemaObjectType)
593                        or v.type.variants):
594                    raise QAPISemError(
595                        self.info,
596                        "%s cannot use %s"
597                        % (v.describe(self.info), v.type.describe()))
598                v.type.check(schema)
599
600    def check_clash(self, info, seen):
601        for v in self.variants:
602            # Reset seen map for each variant, since qapi names from one
603            # branch do not affect another branch
604            v.type.check_clash(info, dict(seen))
605
606
607class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
608    role = 'branch'
609
610    def __init__(self, name, info, typ, ifcond=None):
611        QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
612                                            False, ifcond)
613
614
615class QAPISchemaAlternateType(QAPISchemaType):
616    meta = 'alternate'
617
618    def __init__(self, name, info, doc, ifcond, variants):
619        QAPISchemaType.__init__(self, name, info, doc, ifcond)
620        assert isinstance(variants, QAPISchemaObjectTypeVariants)
621        assert variants.tag_member
622        variants.set_defined_in(name)
623        variants.tag_member.set_defined_in(self.name)
624        self.variants = variants
625
626    def check(self, schema):
627        QAPISchemaType.check(self, schema)
628        self.variants.tag_member.check(schema)
629        # Not calling self.variants.check_clash(), because there's nothing
630        # to clash with
631        self.variants.check(schema, {})
632        # Alternate branch names have no relation to the tag enum values;
633        # so we have to check for potential name collisions ourselves.
634        seen = {}
635        types_seen = {}
636        for v in self.variants.variants:
637            v.check_clash(self.info, seen)
638            qtype = v.type.alternate_qtype()
639            if not qtype:
640                raise QAPISemError(
641                    self.info,
642                    "%s cannot use %s"
643                    % (v.describe(self.info), v.type.describe()))
644            conflicting = set([qtype])
645            if qtype == 'QTYPE_QSTRING':
646                if isinstance(v.type, QAPISchemaEnumType):
647                    for m in v.type.members:
648                        if m.name in ['on', 'off']:
649                            conflicting.add('QTYPE_QBOOL')
650                        if re.match(r'[-+0-9.]', m.name):
651                            # lazy, could be tightened
652                            conflicting.add('QTYPE_QNUM')
653                else:
654                    conflicting.add('QTYPE_QNUM')
655                    conflicting.add('QTYPE_QBOOL')
656            for qt in conflicting:
657                if qt in types_seen:
658                    raise QAPISemError(
659                        self.info,
660                        "%s can't be distinguished from '%s'"
661                        % (v.describe(self.info), types_seen[qt]))
662                types_seen[qt] = v.name
663
664    def connect_doc(self, doc=None):
665        doc = doc or self.doc
666        if doc:
667            for v in self.variants.variants:
668                doc.connect_member(v)
669
670    def check_doc(self):
671        if self.doc:
672            self.doc.check()
673
674    def c_type(self):
675        return c_name(self.name) + pointer_suffix
676
677    def json_type(self):
678        return 'value'
679
680    def visit(self, visitor):
681        QAPISchemaType.visit(self, visitor)
682        visitor.visit_alternate_type(self.name, self.info, self.ifcond,
683                                     self.variants)
684
685
686class QAPISchemaCommand(QAPISchemaEntity):
687    meta = 'command'
688
689    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
690                 gen, success_response, boxed, allow_oob, allow_preconfig,
691                 features):
692        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
693        assert not arg_type or isinstance(arg_type, str)
694        assert not ret_type or isinstance(ret_type, str)
695        for f in features:
696            assert isinstance(f, QAPISchemaFeature)
697            f.set_defined_in(name)
698        self._arg_type_name = arg_type
699        self.arg_type = None
700        self._ret_type_name = ret_type
701        self.ret_type = None
702        self.gen = gen
703        self.success_response = success_response
704        self.boxed = boxed
705        self.allow_oob = allow_oob
706        self.allow_preconfig = allow_preconfig
707        self.features = features
708
709    def check(self, schema):
710        QAPISchemaEntity.check(self, schema)
711        if self._arg_type_name:
712            self.arg_type = schema.resolve_type(
713                self._arg_type_name, self.info, "command's 'data'")
714            if not isinstance(self.arg_type, QAPISchemaObjectType):
715                raise QAPISemError(
716                    self.info,
717                    "command's 'data' cannot take %s"
718                    % self.arg_type.describe())
719            if self.arg_type.variants and not self.boxed:
720                raise QAPISemError(
721                    self.info,
722                    "command's 'data' can take %s only with 'boxed': true"
723                    % self.arg_type.describe())
724        if self._ret_type_name:
725            self.ret_type = schema.resolve_type(
726                self._ret_type_name, self.info, "command's 'returns'")
727            if self.name not in self.info.pragma.returns_whitelist:
728                if not (isinstance(self.ret_type, QAPISchemaObjectType)
729                        or (isinstance(self.ret_type, QAPISchemaArrayType)
730                            and isinstance(self.ret_type.element_type,
731                                           QAPISchemaObjectType))):
732                    raise QAPISemError(
733                        self.info,
734                        "command's 'returns' cannot take %s"
735                        % self.ret_type.describe())
736
737        # Features are in a name space separate from members
738        seen = {}
739        for f in self.features:
740            f.check_clash(self.info, seen)
741
742    def connect_doc(self, doc=None):
743        doc = doc or self.doc
744        if doc:
745            if self.arg_type and self.arg_type.is_implicit():
746                self.arg_type.connect_doc(doc)
747
748    def check_doc(self):
749        if self.doc:
750            self.doc.check()
751
752    def visit(self, visitor):
753        QAPISchemaEntity.visit(self, visitor)
754        visitor.visit_command(self.name, self.info, self.ifcond,
755                              self.arg_type, self.ret_type,
756                              self.gen, self.success_response,
757                              self.boxed, self.allow_oob,
758                              self.allow_preconfig,
759                              self.features)
760
761
762class QAPISchemaEvent(QAPISchemaEntity):
763    meta = 'event'
764
765    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
766        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
767        assert not arg_type or isinstance(arg_type, str)
768        self._arg_type_name = arg_type
769        self.arg_type = None
770        self.boxed = boxed
771
772    def check(self, schema):
773        QAPISchemaEntity.check(self, schema)
774        if self._arg_type_name:
775            self.arg_type = schema.resolve_type(
776                self._arg_type_name, self.info, "event's 'data'")
777            if not isinstance(self.arg_type, QAPISchemaObjectType):
778                raise QAPISemError(
779                    self.info,
780                    "event's 'data' cannot take %s"
781                    % self.arg_type.describe())
782            if self.arg_type.variants and not self.boxed:
783                raise QAPISemError(
784                    self.info,
785                    "event's 'data' can take %s only with 'boxed': true"
786                    % self.arg_type.describe())
787
788    def connect_doc(self, doc=None):
789        doc = doc or self.doc
790        if doc:
791            if self.arg_type and self.arg_type.is_implicit():
792                self.arg_type.connect_doc(doc)
793
794    def check_doc(self):
795        if self.doc:
796            self.doc.check()
797
798    def visit(self, visitor):
799        QAPISchemaEntity.visit(self, visitor)
800        visitor.visit_event(self.name, self.info, self.ifcond,
801                            self.arg_type, self.boxed)
802
803
804class QAPISchema(object):
805    def __init__(self, fname):
806        self.fname = fname
807        parser = QAPISchemaParser(fname)
808        exprs = check_exprs(parser.exprs)
809        self.docs = parser.docs
810        self._entity_list = []
811        self._entity_dict = {}
812        self._predefining = True
813        self._def_predefineds()
814        self._predefining = False
815        self._def_exprs(exprs)
816        self.check()
817
818    def _def_entity(self, ent):
819        # Only the predefined types are allowed to not have info
820        assert ent.info or self._predefining
821        self._entity_list.append(ent)
822        if ent.name is None:
823            return
824        # TODO reject names that differ only in '_' vs. '.'  vs. '-',
825        # because they're liable to clash in generated C.
826        other_ent = self._entity_dict.get(ent.name)
827        if other_ent:
828            if other_ent.info:
829                where = QAPIError(other_ent.info, None, "previous definition")
830                raise QAPISemError(
831                    ent.info,
832                    "'%s' is already defined\n%s" % (ent.name, where))
833            raise QAPISemError(
834                ent.info, "%s is already defined" % other_ent.describe())
835        self._entity_dict[ent.name] = ent
836
837    def lookup_entity(self, name, typ=None):
838        ent = self._entity_dict.get(name)
839        if typ and not isinstance(ent, typ):
840            return None
841        return ent
842
843    def lookup_type(self, name):
844        return self.lookup_entity(name, QAPISchemaType)
845
846    def resolve_type(self, name, info, what):
847        typ = self.lookup_type(name)
848        if not typ:
849            if callable(what):
850                what = what(info)
851            raise QAPISemError(
852                info, "%s uses unknown type '%s'" % (what, name))
853        return typ
854
855    def _def_include(self, expr, info, doc):
856        include = expr['include']
857        assert doc is None
858        main_info = info
859        while main_info.parent:
860            main_info = main_info.parent
861        fname = os.path.relpath(include, os.path.dirname(main_info.fname))
862        self._def_entity(QAPISchemaInclude(fname, info))
863
864    def _def_builtin_type(self, name, json_type, c_type):
865        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
866        # Instantiating only the arrays that are actually used would
867        # be nice, but we can't as long as their generated code
868        # (qapi-builtin-types.[ch]) may be shared by some other
869        # schema.
870        self._make_array_type(name, None)
871
872    def _def_predefineds(self):
873        for t in [('str',    'string',  'char' + pointer_suffix),
874                  ('number', 'number',  'double'),
875                  ('int',    'int',     'int64_t'),
876                  ('int8',   'int',     'int8_t'),
877                  ('int16',  'int',     'int16_t'),
878                  ('int32',  'int',     'int32_t'),
879                  ('int64',  'int',     'int64_t'),
880                  ('uint8',  'int',     'uint8_t'),
881                  ('uint16', 'int',     'uint16_t'),
882                  ('uint32', 'int',     'uint32_t'),
883                  ('uint64', 'int',     'uint64_t'),
884                  ('size',   'int',     'uint64_t'),
885                  ('bool',   'boolean', 'bool'),
886                  ('any',    'value',   'QObject' + pointer_suffix),
887                  ('null',   'null',    'QNull' + pointer_suffix)]:
888            self._def_builtin_type(*t)
889        self.the_empty_object_type = QAPISchemaObjectType(
890            'q_empty', None, None, None, None, [], None, [])
891        self._def_entity(self.the_empty_object_type)
892
893        qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
894                  'qbool']
895        qtype_values = self._make_enum_members(
896            [{'name': n} for n in qtypes], None)
897
898        self._def_entity(QAPISchemaEnumType('QType', None, None, None,
899                                            qtype_values, 'QTYPE'))
900
901    def _make_features(self, features, info):
902        return [QAPISchemaFeature(f['name'], info, f.get('if'))
903                for f in features]
904
905    def _make_enum_members(self, values, info):
906        return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
907                for v in values]
908
909    def _make_implicit_enum_type(self, name, info, ifcond, values):
910        # See also QAPISchemaObjectTypeMember.describe()
911        name = name + 'Kind'    # reserved by check_defn_name_str()
912        self._def_entity(QAPISchemaEnumType(
913            name, info, None, ifcond, self._make_enum_members(values, info),
914            None))
915        return name
916
917    def _make_array_type(self, element_type, info):
918        name = element_type + 'List'    # reserved by check_defn_name_str()
919        if not self.lookup_type(name):
920            self._def_entity(QAPISchemaArrayType(name, info, element_type))
921        return name
922
923    def _make_implicit_object_type(self, name, info, doc, ifcond,
924                                   role, members):
925        if not members:
926            return None
927        # See also QAPISchemaObjectTypeMember.describe()
928        name = 'q_obj_%s-%s' % (name, role)
929        typ = self.lookup_entity(name, QAPISchemaObjectType)
930        if typ:
931            # The implicit object type has multiple users.  This can
932            # happen only for simple unions' implicit wrapper types.
933            # Its ifcond should be the disjunction of its user's
934            # ifconds.  Not implemented.  Instead, we always pass the
935            # wrapped type's ifcond, which is trivially the same for all
936            # users.  It's also necessary for the wrapper to compile.
937            # But it's not tight: the disjunction need not imply it.  We
938            # may end up compiling useless wrapper types.
939            # TODO kill simple unions or implement the disjunction
940            assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
941        else:
942            self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
943                                                  None, members, None, []))
944        return name
945
946    def _def_enum_type(self, expr, info, doc):
947        name = expr['enum']
948        data = expr['data']
949        prefix = expr.get('prefix')
950        ifcond = expr.get('if')
951        self._def_entity(QAPISchemaEnumType(
952            name, info, doc, ifcond,
953            self._make_enum_members(data, info), prefix))
954
955    def _make_member(self, name, typ, ifcond, info):
956        optional = False
957        if name.startswith('*'):
958            name = name[1:]
959            optional = True
960        if isinstance(typ, list):
961            assert len(typ) == 1
962            typ = self._make_array_type(typ[0], info)
963        return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
964
965    def _make_members(self, data, info):
966        return [self._make_member(key, value['type'], value.get('if'), info)
967                for (key, value) in data.items()]
968
969    def _def_struct_type(self, expr, info, doc):
970        name = expr['struct']
971        base = expr.get('base')
972        data = expr['data']
973        ifcond = expr.get('if')
974        features = expr.get('features', [])
975        self._def_entity(QAPISchemaObjectType(
976            name, info, doc, ifcond, base,
977            self._make_members(data, info),
978            None,
979            self._make_features(features, info)))
980
981    def _make_variant(self, case, typ, ifcond, info):
982        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
983
984    def _make_simple_variant(self, case, typ, ifcond, info):
985        if isinstance(typ, list):
986            assert len(typ) == 1
987            typ = self._make_array_type(typ[0], info)
988        typ = self._make_implicit_object_type(
989            typ, info, None, self.lookup_type(typ),
990            'wrapper', [self._make_member('data', typ, None, info)])
991        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
992
993    def _def_union_type(self, expr, info, doc):
994        name = expr['union']
995        data = expr['data']
996        base = expr.get('base')
997        ifcond = expr.get('if')
998        tag_name = expr.get('discriminator')
999        tag_member = None
1000        if isinstance(base, dict):
1001            base = self._make_implicit_object_type(
1002                name, info, None, ifcond,
1003                'base', self._make_members(base, info))
1004        if tag_name:
1005            variants = [self._make_variant(key, value['type'],
1006                                           value.get('if'), info)
1007                        for (key, value) in data.items()]
1008            members = []
1009        else:
1010            variants = [self._make_simple_variant(key, value['type'],
1011                                                  value.get('if'), info)
1012                        for (key, value) in data.items()]
1013            enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1014            typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1015            tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1016            members = [tag_member]
1017        self._def_entity(
1018            QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1019                                 QAPISchemaObjectTypeVariants(
1020                                     tag_name, info, tag_member, variants),
1021                                 []))
1022
1023    def _def_alternate_type(self, expr, info, doc):
1024        name = expr['alternate']
1025        data = expr['data']
1026        ifcond = expr.get('if')
1027        variants = [self._make_variant(key, value['type'], value.get('if'),
1028                                       info)
1029                    for (key, value) in data.items()]
1030        tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1031        self._def_entity(
1032            QAPISchemaAlternateType(name, info, doc, ifcond,
1033                                    QAPISchemaObjectTypeVariants(
1034                                        None, info, tag_member, variants)))
1035
1036    def _def_command(self, expr, info, doc):
1037        name = expr['command']
1038        data = expr.get('data')
1039        rets = expr.get('returns')
1040        gen = expr.get('gen', True)
1041        success_response = expr.get('success-response', True)
1042        boxed = expr.get('boxed', False)
1043        allow_oob = expr.get('allow-oob', False)
1044        allow_preconfig = expr.get('allow-preconfig', False)
1045        ifcond = expr.get('if')
1046        features = expr.get('features', [])
1047        if isinstance(data, OrderedDict):
1048            data = self._make_implicit_object_type(
1049                name, info, None, ifcond, 'arg', self._make_members(data, info))
1050        if isinstance(rets, list):
1051            assert len(rets) == 1
1052            rets = self._make_array_type(rets[0], info)
1053        self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1054                                           gen, success_response,
1055                                           boxed, allow_oob, allow_preconfig,
1056                                           self._make_features(features, info)))
1057
1058    def _def_event(self, expr, info, doc):
1059        name = expr['event']
1060        data = expr.get('data')
1061        boxed = expr.get('boxed', False)
1062        ifcond = expr.get('if')
1063        if isinstance(data, OrderedDict):
1064            data = self._make_implicit_object_type(
1065                name, info, None, ifcond, 'arg', self._make_members(data, info))
1066        self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1067
1068    def _def_exprs(self, exprs):
1069        for expr_elem in exprs:
1070            expr = expr_elem['expr']
1071            info = expr_elem['info']
1072            doc = expr_elem.get('doc')
1073            if 'enum' in expr:
1074                self._def_enum_type(expr, info, doc)
1075            elif 'struct' in expr:
1076                self._def_struct_type(expr, info, doc)
1077            elif 'union' in expr:
1078                self._def_union_type(expr, info, doc)
1079            elif 'alternate' in expr:
1080                self._def_alternate_type(expr, info, doc)
1081            elif 'command' in expr:
1082                self._def_command(expr, info, doc)
1083            elif 'event' in expr:
1084                self._def_event(expr, info, doc)
1085            elif 'include' in expr:
1086                self._def_include(expr, info, doc)
1087            else:
1088                assert False
1089
1090    def check(self):
1091        for ent in self._entity_list:
1092            ent.check(self)
1093            ent.connect_doc()
1094            ent.check_doc()
1095
1096    def visit(self, visitor):
1097        visitor.visit_begin(self)
1098        module = None
1099        visitor.visit_module(module)
1100        for entity in self._entity_list:
1101            if visitor.visit_needed(entity):
1102                if entity.module != module:
1103                    module = entity.module
1104                    visitor.visit_module(module)
1105                entity.visit(visitor)
1106        visitor.visit_end()
1107