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