xref: /openbmc/qemu/scripts/qapi/schema.py (revision a9f1dd7ee001b645b81ad67217b582e51a44d545)
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, features=None):
31        assert name is None or isinstance(name, str)
32        for f in features or []:
33            assert isinstance(f, QAPISchemaFeature)
34            f.set_defined_in(name)
35        self.name = name
36        self._module = None
37        # For explicitly defined entities, info points to the (explicit)
38        # definition.  For builtins (and their arrays), info is None.
39        # For implicitly defined entities, info points to a place that
40        # triggered the implicit definition (there may be more than one
41        # such place).
42        self.info = info
43        self.doc = doc
44        self._ifcond = ifcond or []
45        self.features = features or []
46        self._checked = False
47
48    def c_name(self):
49        return c_name(self.name)
50
51    def check(self, schema):
52        assert not self._checked
53        seen = {}
54        for f in self.features:
55            f.check_clash(self.info, seen)
56            if self.doc:
57                self.doc.connect_feature(f)
58
59        self._checked = True
60
61    def connect_doc(self, doc=None):
62        pass
63
64    def check_doc(self):
65        if self.doc:
66            self.doc.check()
67
68    def _set_module(self, schema, info):
69        assert self._checked
70        self._module = schema.module_by_fname(info and info.fname)
71
72    def set_module(self, schema):
73        self._set_module(schema, self.info)
74
75    @property
76    def ifcond(self):
77        assert self._checked
78        return self._ifcond
79
80    @property
81    def module(self):
82        assert self._module or not self.info
83        return self._module
84
85    def is_implicit(self):
86        return not self.info
87
88    def visit(self, visitor):
89        assert self._checked
90
91    def describe(self):
92        assert self.meta
93        return "%s '%s'" % (self.meta, self.name)
94
95
96class QAPISchemaVisitor(object):
97    def visit_begin(self, schema):
98        pass
99
100    def visit_end(self):
101        pass
102
103    def visit_module(self, fname):
104        pass
105
106    def visit_needed(self, entity):
107        # Default to visiting everything
108        return True
109
110    def visit_include(self, fname, info):
111        pass
112
113    def visit_builtin_type(self, name, info, json_type):
114        pass
115
116    def visit_enum_type(self, name, info, ifcond, members, prefix):
117        pass
118
119    def visit_array_type(self, name, info, ifcond, element_type):
120        pass
121
122    def visit_object_type(self, name, info, ifcond, base, members, variants,
123                          features):
124        pass
125
126    def visit_object_type_flat(self, name, info, ifcond, members, variants,
127                               features):
128        pass
129
130    def visit_alternate_type(self, name, info, ifcond, variants):
131        pass
132
133    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
134                      success_response, boxed, allow_oob, allow_preconfig,
135                      features):
136        pass
137
138    def visit_event(self, name, info, ifcond, arg_type, boxed):
139        pass
140
141
142class QAPISchemaModule(object):
143    def __init__(self, name):
144        self.name = name
145
146
147class QAPISchemaInclude(QAPISchemaEntity):
148    def __init__(self, sub_module, info):
149        QAPISchemaEntity.__init__(self, None, info, None)
150        self._sub_module = sub_module
151
152    def visit(self, visitor):
153        QAPISchemaEntity.visit(self, visitor)
154        visitor.visit_include(self._sub_module.name, self.info)
155
156
157class QAPISchemaType(QAPISchemaEntity):
158    # Return the C type for common use.
159    # For the types we commonly box, this is a pointer type.
160    def c_type(self):
161        pass
162
163    # Return the C type to be used in a parameter list.
164    def c_param_type(self):
165        return self.c_type()
166
167    # Return the C type to be used where we suppress boxing.
168    def c_unboxed_type(self):
169        return self.c_type()
170
171    def json_type(self):
172        pass
173
174    def alternate_qtype(self):
175        json2qtype = {
176            'null':    'QTYPE_QNULL',
177            'string':  'QTYPE_QSTRING',
178            'number':  'QTYPE_QNUM',
179            'int':     'QTYPE_QNUM',
180            'boolean': 'QTYPE_QBOOL',
181            'object':  'QTYPE_QDICT'
182        }
183        return json2qtype.get(self.json_type())
184
185    def doc_type(self):
186        if self.is_implicit():
187            return None
188        return self.name
189
190    def describe(self):
191        assert self.meta
192        return "%s type '%s'" % (self.meta, self.name)
193
194
195class QAPISchemaBuiltinType(QAPISchemaType):
196    meta = 'built-in'
197
198    def __init__(self, name, json_type, c_type):
199        QAPISchemaType.__init__(self, name, None, None)
200        assert not c_type or isinstance(c_type, str)
201        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
202                             'value')
203        self._json_type_name = json_type
204        self._c_type_name = c_type
205
206    def c_name(self):
207        return self.name
208
209    def c_type(self):
210        return self._c_type_name
211
212    def c_param_type(self):
213        if self.name == 'str':
214            return 'const ' + self._c_type_name
215        return self._c_type_name
216
217    def json_type(self):
218        return self._json_type_name
219
220    def doc_type(self):
221        return self.json_type()
222
223    def visit(self, visitor):
224        QAPISchemaType.visit(self, visitor)
225        visitor.visit_builtin_type(self.name, self.info, self.json_type())
226
227
228class QAPISchemaEnumType(QAPISchemaType):
229    meta = 'enum'
230
231    def __init__(self, name, info, doc, ifcond, members, prefix):
232        QAPISchemaType.__init__(self, name, info, doc, ifcond)
233        for m in members:
234            assert isinstance(m, QAPISchemaEnumMember)
235            m.set_defined_in(name)
236        assert prefix is None or isinstance(prefix, str)
237        self.members = members
238        self.prefix = prefix
239
240    def check(self, schema):
241        QAPISchemaType.check(self, schema)
242        seen = {}
243        for m in self.members:
244            m.check_clash(self.info, seen)
245
246    def connect_doc(self, doc=None):
247        doc = doc or self.doc
248        if doc:
249            for m in self.members:
250                doc.connect_member(m)
251
252    def is_implicit(self):
253        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
254        return self.name.endswith('Kind') or self.name == 'QType'
255
256    def c_type(self):
257        return c_name(self.name)
258
259    def member_names(self):
260        return [m.name for m in self.members]
261
262    def json_type(self):
263        return 'string'
264
265    def visit(self, visitor):
266        QAPISchemaType.visit(self, visitor)
267        visitor.visit_enum_type(self.name, self.info, self.ifcond,
268                                self.members, self.prefix)
269
270
271class QAPISchemaArrayType(QAPISchemaType):
272    meta = 'array'
273
274    def __init__(self, name, info, element_type):
275        QAPISchemaType.__init__(self, name, info, None, None)
276        assert isinstance(element_type, str)
277        self._element_type_name = element_type
278        self.element_type = None
279
280    def check(self, schema):
281        QAPISchemaType.check(self, schema)
282        self.element_type = schema.resolve_type(
283            self._element_type_name, self.info,
284            self.info and self.info.defn_meta)
285        assert not isinstance(self.element_type, QAPISchemaArrayType)
286
287    def set_module(self, schema):
288        self._set_module(schema, self.element_type.info)
289
290    @property
291    def ifcond(self):
292        assert self._checked
293        return self.element_type.ifcond
294
295    def is_implicit(self):
296        return True
297
298    def c_type(self):
299        return c_name(self.name) + pointer_suffix
300
301    def json_type(self):
302        return 'array'
303
304    def doc_type(self):
305        elt_doc_type = self.element_type.doc_type()
306        if not elt_doc_type:
307            return None
308        return 'array of ' + elt_doc_type
309
310    def visit(self, visitor):
311        QAPISchemaType.visit(self, visitor)
312        visitor.visit_array_type(self.name, self.info, self.ifcond,
313                                 self.element_type)
314
315    def describe(self):
316        assert self.meta
317        return "%s type ['%s']" % (self.meta, self._element_type_name)
318
319
320class QAPISchemaObjectType(QAPISchemaType):
321    def __init__(self, name, info, doc, ifcond,
322                 base, local_members, variants, features):
323        # struct has local_members, optional base, and no variants
324        # flat union has base, variants, and no local_members
325        # simple union has local_members, variants, and no base
326        QAPISchemaType.__init__(self, name, info, doc, ifcond, features)
327        self.meta = 'union' if variants else 'struct'
328        assert base is None or isinstance(base, str)
329        for m in local_members:
330            assert isinstance(m, QAPISchemaObjectTypeMember)
331            m.set_defined_in(name)
332        if variants is not None:
333            assert isinstance(variants, QAPISchemaObjectTypeVariants)
334            variants.set_defined_in(name)
335        self._base_name = base
336        self.base = None
337        self.local_members = local_members
338        self.variants = variants
339        self.members = None
340
341    def check(self, schema):
342        # This calls another type T's .check() exactly when the C
343        # struct emitted by gen_object() contains that T's C struct
344        # (pointers don't count).
345        if self.members is not None:
346            # A previous .check() completed: nothing to do
347            return
348        if self._checked:
349            # Recursed: C struct contains itself
350            raise QAPISemError(self.info,
351                               "object %s contains itself" % self.name)
352
353        QAPISchemaType.check(self, schema)
354        assert self._checked and self.members is None
355
356        seen = OrderedDict()
357        if self._base_name:
358            self.base = schema.resolve_type(self._base_name, self.info,
359                                            "'base'")
360            if (not isinstance(self.base, QAPISchemaObjectType)
361                    or self.base.variants):
362                raise QAPISemError(
363                    self.info,
364                    "'base' requires a struct type, %s isn't"
365                    % self.base.describe())
366            self.base.check(schema)
367            self.base.check_clash(self.info, seen)
368        for m in self.local_members:
369            m.check(schema)
370            m.check_clash(self.info, seen)
371        members = seen.values()
372
373        if self.variants:
374            self.variants.check(schema, seen)
375            self.variants.check_clash(self.info, seen)
376
377        self.members = members  # mark completed
378
379    # Check that the members of this type do not cause duplicate JSON members,
380    # and update seen to track the members seen so far. Report any errors
381    # on behalf of info, which is not necessarily self.info
382    def check_clash(self, info, seen):
383        assert self._checked
384        assert not self.variants       # not implemented
385        for m in self.members:
386            m.check_clash(info, seen)
387
388    def connect_doc(self, doc=None):
389        doc = doc or self.doc
390        if doc:
391            if self.base and self.base.is_implicit():
392                self.base.connect_doc(doc)
393            for m in self.local_members:
394                doc.connect_member(m)
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 c_type(self):
671        return c_name(self.name) + pointer_suffix
672
673    def json_type(self):
674        return 'value'
675
676    def visit(self, visitor):
677        QAPISchemaType.visit(self, visitor)
678        visitor.visit_alternate_type(self.name, self.info, self.ifcond,
679                                     self.variants)
680
681
682class QAPISchemaCommand(QAPISchemaEntity):
683    meta = 'command'
684
685    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
686                 gen, success_response, boxed, allow_oob, allow_preconfig,
687                 features):
688        QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features)
689        assert not arg_type or isinstance(arg_type, str)
690        assert not ret_type or isinstance(ret_type, str)
691        self._arg_type_name = arg_type
692        self.arg_type = None
693        self._ret_type_name = ret_type
694        self.ret_type = None
695        self.gen = gen
696        self.success_response = success_response
697        self.boxed = boxed
698        self.allow_oob = allow_oob
699        self.allow_preconfig = allow_preconfig
700
701    def check(self, schema):
702        QAPISchemaEntity.check(self, schema)
703        if self._arg_type_name:
704            self.arg_type = schema.resolve_type(
705                self._arg_type_name, self.info, "command's 'data'")
706            if not isinstance(self.arg_type, QAPISchemaObjectType):
707                raise QAPISemError(
708                    self.info,
709                    "command's 'data' cannot take %s"
710                    % self.arg_type.describe())
711            if self.arg_type.variants and not self.boxed:
712                raise QAPISemError(
713                    self.info,
714                    "command's 'data' can take %s only with 'boxed': true"
715                    % self.arg_type.describe())
716        if self._ret_type_name:
717            self.ret_type = schema.resolve_type(
718                self._ret_type_name, self.info, "command's 'returns'")
719            if self.name not in self.info.pragma.returns_whitelist:
720                typ = self.ret_type
721                if isinstance(typ, QAPISchemaArrayType):
722                    typ = self.ret_type.element_type
723                    assert typ
724                if not isinstance(typ, QAPISchemaObjectType):
725                    raise QAPISemError(
726                        self.info,
727                        "command's 'returns' cannot take %s"
728                        % self.ret_type.describe())
729
730    def connect_doc(self, doc=None):
731        doc = doc or self.doc
732        if doc:
733            if self.arg_type and self.arg_type.is_implicit():
734                self.arg_type.connect_doc(doc)
735
736    def visit(self, visitor):
737        QAPISchemaEntity.visit(self, visitor)
738        visitor.visit_command(self.name, self.info, self.ifcond,
739                              self.arg_type, self.ret_type,
740                              self.gen, self.success_response,
741                              self.boxed, self.allow_oob,
742                              self.allow_preconfig,
743                              self.features)
744
745
746class QAPISchemaEvent(QAPISchemaEntity):
747    meta = 'event'
748
749    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
750        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
751        assert not arg_type or isinstance(arg_type, str)
752        self._arg_type_name = arg_type
753        self.arg_type = None
754        self.boxed = boxed
755
756    def check(self, schema):
757        QAPISchemaEntity.check(self, schema)
758        if self._arg_type_name:
759            self.arg_type = schema.resolve_type(
760                self._arg_type_name, self.info, "event's 'data'")
761            if not isinstance(self.arg_type, QAPISchemaObjectType):
762                raise QAPISemError(
763                    self.info,
764                    "event's 'data' cannot take %s"
765                    % self.arg_type.describe())
766            if self.arg_type.variants and not self.boxed:
767                raise QAPISemError(
768                    self.info,
769                    "event's 'data' can take %s only with 'boxed': true"
770                    % self.arg_type.describe())
771
772    def connect_doc(self, doc=None):
773        doc = doc or self.doc
774        if doc:
775            if self.arg_type and self.arg_type.is_implicit():
776                self.arg_type.connect_doc(doc)
777
778    def visit(self, visitor):
779        QAPISchemaEntity.visit(self, visitor)
780        visitor.visit_event(self.name, self.info, self.ifcond,
781                            self.arg_type, self.boxed)
782
783
784class QAPISchema(object):
785    def __init__(self, fname):
786        self.fname = fname
787        parser = QAPISchemaParser(fname)
788        exprs = check_exprs(parser.exprs)
789        self.docs = parser.docs
790        self._entity_list = []
791        self._entity_dict = {}
792        self._module_dict = {}
793        self._schema_dir = os.path.dirname(fname)
794        self._make_module(None) # built-ins
795        self._make_module(fname)
796        self._predefining = True
797        self._def_predefineds()
798        self._predefining = False
799        self._def_exprs(exprs)
800        self.check()
801
802    def _def_entity(self, ent):
803        # Only the predefined types are allowed to not have info
804        assert ent.info or self._predefining
805        self._entity_list.append(ent)
806        if ent.name is None:
807            return
808        # TODO reject names that differ only in '_' vs. '.'  vs. '-',
809        # because they're liable to clash in generated C.
810        other_ent = self._entity_dict.get(ent.name)
811        if other_ent:
812            if other_ent.info:
813                where = QAPIError(other_ent.info, None, "previous definition")
814                raise QAPISemError(
815                    ent.info,
816                    "'%s' is already defined\n%s" % (ent.name, where))
817            raise QAPISemError(
818                ent.info, "%s is already defined" % other_ent.describe())
819        self._entity_dict[ent.name] = ent
820
821    def lookup_entity(self, name, typ=None):
822        ent = self._entity_dict.get(name)
823        if typ and not isinstance(ent, typ):
824            return None
825        return ent
826
827    def lookup_type(self, name):
828        return self.lookup_entity(name, QAPISchemaType)
829
830    def resolve_type(self, name, info, what):
831        typ = self.lookup_type(name)
832        if not typ:
833            if callable(what):
834                what = what(info)
835            raise QAPISemError(
836                info, "%s uses unknown type '%s'" % (what, name))
837        return typ
838
839    def _module_name(self, fname):
840        if fname is None:
841            return None
842        return os.path.relpath(fname, self._schema_dir)
843
844    def _make_module(self, fname):
845        name = self._module_name(fname)
846        if not name in self._module_dict:
847            self._module_dict[name] = QAPISchemaModule(name)
848        return self._module_dict[name]
849
850    def module_by_fname(self, fname):
851        name = self._module_name(fname)
852        assert name in self._module_dict
853        return self._module_dict[name]
854
855    def _def_include(self, expr, info, doc):
856        include = expr['include']
857        assert doc is None
858        self._def_entity(QAPISchemaInclude(self._make_module(include), info))
859
860    def _def_builtin_type(self, name, json_type, c_type):
861        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
862        # Instantiating only the arrays that are actually used would
863        # be nice, but we can't as long as their generated code
864        # (qapi-builtin-types.[ch]) may be shared by some other
865        # schema.
866        self._make_array_type(name, None)
867
868    def _def_predefineds(self):
869        for t in [('str',    'string',  'char' + pointer_suffix),
870                  ('number', 'number',  'double'),
871                  ('int',    'int',     'int64_t'),
872                  ('int8',   'int',     'int8_t'),
873                  ('int16',  'int',     'int16_t'),
874                  ('int32',  'int',     'int32_t'),
875                  ('int64',  'int',     'int64_t'),
876                  ('uint8',  'int',     'uint8_t'),
877                  ('uint16', 'int',     'uint16_t'),
878                  ('uint32', 'int',     'uint32_t'),
879                  ('uint64', 'int',     'uint64_t'),
880                  ('size',   'int',     'uint64_t'),
881                  ('bool',   'boolean', 'bool'),
882                  ('any',    'value',   'QObject' + pointer_suffix),
883                  ('null',   'null',    'QNull' + pointer_suffix)]:
884            self._def_builtin_type(*t)
885        self.the_empty_object_type = QAPISchemaObjectType(
886            'q_empty', None, None, None, None, [], None, [])
887        self._def_entity(self.the_empty_object_type)
888
889        qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
890                  'qbool']
891        qtype_values = self._make_enum_members(
892            [{'name': n} for n in qtypes], None)
893
894        self._def_entity(QAPISchemaEnumType('QType', None, None, None,
895                                            qtype_values, 'QTYPE'))
896
897    def _make_features(self, features, info):
898        return [QAPISchemaFeature(f['name'], info, f.get('if'))
899                for f in features]
900
901    def _make_enum_members(self, values, info):
902        return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
903                for v in values]
904
905    def _make_implicit_enum_type(self, name, info, ifcond, values):
906        # See also QAPISchemaObjectTypeMember.describe()
907        name = name + 'Kind'    # reserved by check_defn_name_str()
908        self._def_entity(QAPISchemaEnumType(
909            name, info, None, ifcond, self._make_enum_members(values, info),
910            None))
911        return name
912
913    def _make_array_type(self, element_type, info):
914        name = element_type + 'List'    # reserved by check_defn_name_str()
915        if not self.lookup_type(name):
916            self._def_entity(QAPISchemaArrayType(name, info, element_type))
917        return name
918
919    def _make_implicit_object_type(self, name, info, ifcond, role, members):
920        if not members:
921            return None
922        # See also QAPISchemaObjectTypeMember.describe()
923        name = 'q_obj_%s-%s' % (name, role)
924        typ = self.lookup_entity(name, QAPISchemaObjectType)
925        if typ:
926            # The implicit object type has multiple users.  This can
927            # happen only for simple unions' implicit wrapper types.
928            # Its ifcond should be the disjunction of its user's
929            # ifconds.  Not implemented.  Instead, we always pass the
930            # wrapped type's ifcond, which is trivially the same for all
931            # users.  It's also necessary for the wrapper to compile.
932            # But it's not tight: the disjunction need not imply it.  We
933            # may end up compiling useless wrapper types.
934            # TODO kill simple unions or implement the disjunction
935            assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
936        else:
937            self._def_entity(QAPISchemaObjectType(name, info, None, ifcond,
938                                                  None, members, None, []))
939        return name
940
941    def _def_enum_type(self, expr, info, doc):
942        name = expr['enum']
943        data = expr['data']
944        prefix = expr.get('prefix')
945        ifcond = expr.get('if')
946        self._def_entity(QAPISchemaEnumType(
947            name, info, doc, ifcond,
948            self._make_enum_members(data, info), prefix))
949
950    def _make_member(self, name, typ, ifcond, info):
951        optional = False
952        if name.startswith('*'):
953            name = name[1:]
954            optional = True
955        if isinstance(typ, list):
956            assert len(typ) == 1
957            typ = self._make_array_type(typ[0], info)
958        return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
959
960    def _make_members(self, data, info):
961        return [self._make_member(key, value['type'], value.get('if'), info)
962                for (key, value) in data.items()]
963
964    def _def_struct_type(self, expr, info, doc):
965        name = expr['struct']
966        base = expr.get('base')
967        data = expr['data']
968        ifcond = expr.get('if')
969        features = expr.get('features', [])
970        self._def_entity(QAPISchemaObjectType(
971            name, info, doc, ifcond, base,
972            self._make_members(data, info),
973            None,
974            self._make_features(features, info)))
975
976    def _make_variant(self, case, typ, ifcond, info):
977        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
978
979    def _make_simple_variant(self, case, typ, ifcond, info):
980        if isinstance(typ, list):
981            assert len(typ) == 1
982            typ = self._make_array_type(typ[0], info)
983        typ = self._make_implicit_object_type(
984            typ, info, self.lookup_type(typ),
985            'wrapper', [self._make_member('data', typ, None, info)])
986        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
987
988    def _def_union_type(self, expr, info, doc):
989        name = expr['union']
990        data = expr['data']
991        base = expr.get('base')
992        ifcond = expr.get('if')
993        tag_name = expr.get('discriminator')
994        tag_member = None
995        if isinstance(base, dict):
996            base = self._make_implicit_object_type(
997                name, info, ifcond,
998                'base', self._make_members(base, info))
999        if tag_name:
1000            variants = [self._make_variant(key, value['type'],
1001                                           value.get('if'), info)
1002                        for (key, value) in data.items()]
1003            members = []
1004        else:
1005            variants = [self._make_simple_variant(key, value['type'],
1006                                                  value.get('if'), info)
1007                        for (key, value) in data.items()]
1008            enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1009            typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1010            tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1011            members = [tag_member]
1012        self._def_entity(
1013            QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1014                                 QAPISchemaObjectTypeVariants(
1015                                     tag_name, info, tag_member, variants),
1016                                 []))
1017
1018    def _def_alternate_type(self, expr, info, doc):
1019        name = expr['alternate']
1020        data = expr['data']
1021        ifcond = expr.get('if')
1022        variants = [self._make_variant(key, value['type'], value.get('if'),
1023                                       info)
1024                    for (key, value) in data.items()]
1025        tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1026        self._def_entity(
1027            QAPISchemaAlternateType(name, info, doc, ifcond,
1028                                    QAPISchemaObjectTypeVariants(
1029                                        None, info, tag_member, variants)))
1030
1031    def _def_command(self, expr, info, doc):
1032        name = expr['command']
1033        data = expr.get('data')
1034        rets = expr.get('returns')
1035        gen = expr.get('gen', True)
1036        success_response = expr.get('success-response', True)
1037        boxed = expr.get('boxed', False)
1038        allow_oob = expr.get('allow-oob', False)
1039        allow_preconfig = expr.get('allow-preconfig', False)
1040        ifcond = expr.get('if')
1041        features = expr.get('features', [])
1042        if isinstance(data, OrderedDict):
1043            data = self._make_implicit_object_type(
1044                name, info, ifcond, 'arg', self._make_members(data, info))
1045        if isinstance(rets, list):
1046            assert len(rets) == 1
1047            rets = self._make_array_type(rets[0], info)
1048        self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1049                                           gen, success_response,
1050                                           boxed, allow_oob, allow_preconfig,
1051                                           self._make_features(features, info)))
1052
1053    def _def_event(self, expr, info, doc):
1054        name = expr['event']
1055        data = expr.get('data')
1056        boxed = expr.get('boxed', False)
1057        ifcond = expr.get('if')
1058        if isinstance(data, OrderedDict):
1059            data = self._make_implicit_object_type(
1060                name, info, ifcond, 'arg', self._make_members(data, info))
1061        self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1062
1063    def _def_exprs(self, exprs):
1064        for expr_elem in exprs:
1065            expr = expr_elem['expr']
1066            info = expr_elem['info']
1067            doc = expr_elem.get('doc')
1068            if 'enum' in expr:
1069                self._def_enum_type(expr, info, doc)
1070            elif 'struct' in expr:
1071                self._def_struct_type(expr, info, doc)
1072            elif 'union' in expr:
1073                self._def_union_type(expr, info, doc)
1074            elif 'alternate' in expr:
1075                self._def_alternate_type(expr, info, doc)
1076            elif 'command' in expr:
1077                self._def_command(expr, info, doc)
1078            elif 'event' in expr:
1079                self._def_event(expr, info, doc)
1080            elif 'include' in expr:
1081                self._def_include(expr, info, doc)
1082            else:
1083                assert False
1084
1085    def check(self):
1086        for ent in self._entity_list:
1087            ent.check(self)
1088            ent.connect_doc()
1089            ent.check_doc()
1090        for ent in self._entity_list:
1091            ent.set_module(self)
1092
1093    def visit(self, visitor):
1094        visitor.visit_begin(self)
1095        module = None
1096        for entity in self._entity_list:
1097            if visitor.visit_needed(entity):
1098                if entity.module != module:
1099                    module = entity.module
1100                    visitor.visit_module(module.name)
1101                entity.visit(visitor)
1102        visitor.visit_end()
1103