xref: /qemu/scripts/qapi/introspect.py (revision eb815e24)
1fb0bc835SMarkus Armbruster"""
2fb0bc835SMarkus ArmbrusterQAPI introspection generator
3fb0bc835SMarkus Armbruster
4fb0bc835SMarkus ArmbrusterCopyright (C) 2015-2018 Red Hat, Inc.
5fb0bc835SMarkus Armbruster
6fb0bc835SMarkus ArmbrusterAuthors:
7fb0bc835SMarkus Armbruster Markus Armbruster <armbru@redhat.com>
8fb0bc835SMarkus Armbruster
9fb0bc835SMarkus ArmbrusterThis work is licensed under the terms of the GNU GPL, version 2.
10fb0bc835SMarkus ArmbrusterSee the COPYING file in the top-level directory.
11fb0bc835SMarkus Armbruster"""
12fb0bc835SMarkus Armbruster
13fb0bc835SMarkus Armbrusterfrom qapi.common import *
14fb0bc835SMarkus Armbruster
15fb0bc835SMarkus Armbruster
16fb0bc835SMarkus Armbruster# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
17fb0bc835SMarkus Armbruster# TODO try to use json.dumps() once we get unstuck
18fb0bc835SMarkus Armbrusterdef to_json(obj, level=0):
19fb0bc835SMarkus Armbruster    if obj is None:
20fb0bc835SMarkus Armbruster        ret = 'null'
21fb0bc835SMarkus Armbruster    elif isinstance(obj, str):
22fb0bc835SMarkus Armbruster        ret = '"' + obj.replace('"', r'\"') + '"'
23fb0bc835SMarkus Armbruster    elif isinstance(obj, list):
24fb0bc835SMarkus Armbruster        elts = [to_json(elt, level + 1)
25fb0bc835SMarkus Armbruster                for elt in obj]
26fb0bc835SMarkus Armbruster        ret = '[' + ', '.join(elts) + ']'
27fb0bc835SMarkus Armbruster    elif isinstance(obj, dict):
28fb0bc835SMarkus Armbruster        elts = ['"%s": %s' % (key.replace('"', r'\"'),
29fb0bc835SMarkus Armbruster                              to_json(obj[key], level + 1))
30fb0bc835SMarkus Armbruster                for key in sorted(obj.keys())]
31fb0bc835SMarkus Armbruster        ret = '{' + ', '.join(elts) + '}'
32fb0bc835SMarkus Armbruster    else:
33fb0bc835SMarkus Armbruster        assert False                # not implemented
34fb0bc835SMarkus Armbruster    if level == 1:
35fb0bc835SMarkus Armbruster        ret = '\n' + ret
36fb0bc835SMarkus Armbruster    return ret
37fb0bc835SMarkus Armbruster
38fb0bc835SMarkus Armbruster
39fb0bc835SMarkus Armbrusterdef to_c_string(string):
40fb0bc835SMarkus Armbruster    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
41fb0bc835SMarkus Armbruster
42fb0bc835SMarkus Armbruster
4371b3f045SMarkus Armbrusterclass QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
44fb0bc835SMarkus Armbruster
4571b3f045SMarkus Armbruster    def __init__(self, prefix, unmask):
4671b3f045SMarkus Armbruster        QAPISchemaMonolithicCVisitor.__init__(
47*eb815e24SMarkus Armbruster            self, prefix, 'qapi-introspect',
4871b3f045SMarkus Armbruster            ' * QAPI/QMP schema introspection', __doc__)
4971b3f045SMarkus Armbruster        self._unmask = unmask
5071b3f045SMarkus Armbruster        self._schema = None
51fb0bc835SMarkus Armbruster        self._jsons = []
52fb0bc835SMarkus Armbruster        self._used_types = []
53fb0bc835SMarkus Armbruster        self._name_map = {}
5471b3f045SMarkus Armbruster        self._genc.add(mcgen('''
5571b3f045SMarkus Armbruster#include "qemu/osdep.h"
56*eb815e24SMarkus Armbruster#include "%(prefix)sqapi-introspect.h"
5771b3f045SMarkus Armbruster
5871b3f045SMarkus Armbruster''',
5971b3f045SMarkus Armbruster                             prefix=prefix))
6071b3f045SMarkus Armbruster
6171b3f045SMarkus Armbruster    def visit_begin(self, schema):
6271b3f045SMarkus Armbruster        self._schema = schema
63fb0bc835SMarkus Armbruster
64fb0bc835SMarkus Armbruster    def visit_end(self):
65fb0bc835SMarkus Armbruster        # visit the types that are actually used
66fb0bc835SMarkus Armbruster        jsons = self._jsons
67fb0bc835SMarkus Armbruster        self._jsons = []
68fb0bc835SMarkus Armbruster        for typ in self._used_types:
69fb0bc835SMarkus Armbruster            typ.visit(self)
70fb0bc835SMarkus Armbruster        # generate C
71fb0bc835SMarkus Armbruster        # TODO can generate awfully long lines
72fb0bc835SMarkus Armbruster        jsons.extend(self._jsons)
73fb0bc835SMarkus Armbruster        name = c_name(self._prefix, protect=False) + 'qmp_schema_json'
7471b3f045SMarkus Armbruster        self._genh.add(mcgen('''
75fb0bc835SMarkus Armbrusterextern const char %(c_name)s[];
76fb0bc835SMarkus Armbruster''',
7771b3f045SMarkus Armbruster                             c_name=c_name(name)))
78fb0bc835SMarkus Armbruster        lines = to_json(jsons).split('\n')
79fb0bc835SMarkus Armbruster        c_string = '\n    '.join([to_c_string(line) for line in lines])
8071b3f045SMarkus Armbruster        self._genc.add(mcgen('''
81fb0bc835SMarkus Armbrusterconst char %(c_name)s[] = %(c_string)s;
82fb0bc835SMarkus Armbruster''',
83fb0bc835SMarkus Armbruster                             c_name=c_name(name),
8471b3f045SMarkus Armbruster                             c_string=c_string))
85fb0bc835SMarkus Armbruster        self._schema = None
8671b3f045SMarkus Armbruster        self._jsons = []
8771b3f045SMarkus Armbruster        self._used_types = []
8871b3f045SMarkus Armbruster        self._name_map = {}
89fb0bc835SMarkus Armbruster
90fb0bc835SMarkus Armbruster    def visit_needed(self, entity):
91fb0bc835SMarkus Armbruster        # Ignore types on first pass; visit_end() will pick up used types
92fb0bc835SMarkus Armbruster        return not isinstance(entity, QAPISchemaType)
93fb0bc835SMarkus Armbruster
94fb0bc835SMarkus Armbruster    def _name(self, name):
95fb0bc835SMarkus Armbruster        if self._unmask:
96fb0bc835SMarkus Armbruster            return name
97fb0bc835SMarkus Armbruster        if name not in self._name_map:
98fb0bc835SMarkus Armbruster            self._name_map[name] = '%d' % len(self._name_map)
99fb0bc835SMarkus Armbruster        return self._name_map[name]
100fb0bc835SMarkus Armbruster
101fb0bc835SMarkus Armbruster    def _use_type(self, typ):
102fb0bc835SMarkus Armbruster        # Map the various integer types to plain int
103fb0bc835SMarkus Armbruster        if typ.json_type() == 'int':
104fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('int')
105fb0bc835SMarkus Armbruster        elif (isinstance(typ, QAPISchemaArrayType) and
106fb0bc835SMarkus Armbruster              typ.element_type.json_type() == 'int'):
107fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('intList')
108fb0bc835SMarkus Armbruster        # Add type to work queue if new
109fb0bc835SMarkus Armbruster        if typ not in self._used_types:
110fb0bc835SMarkus Armbruster            self._used_types.append(typ)
111fb0bc835SMarkus Armbruster        # Clients should examine commands and events, not types.  Hide
112fb0bc835SMarkus Armbruster        # type names to reduce the temptation.  Also saves a few
113fb0bc835SMarkus Armbruster        # characters.
114fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaBuiltinType):
115fb0bc835SMarkus Armbruster            return typ.name
116fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaArrayType):
117fb0bc835SMarkus Armbruster            return '[' + self._use_type(typ.element_type) + ']'
118fb0bc835SMarkus Armbruster        return self._name(typ.name)
119fb0bc835SMarkus Armbruster
120fb0bc835SMarkus Armbruster    def _gen_json(self, name, mtype, obj):
121fb0bc835SMarkus Armbruster        if mtype not in ('command', 'event', 'builtin', 'array'):
122fb0bc835SMarkus Armbruster            name = self._name(name)
123fb0bc835SMarkus Armbruster        obj['name'] = name
124fb0bc835SMarkus Armbruster        obj['meta-type'] = mtype
125fb0bc835SMarkus Armbruster        self._jsons.append(obj)
126fb0bc835SMarkus Armbruster
127fb0bc835SMarkus Armbruster    def _gen_member(self, member):
128fb0bc835SMarkus Armbruster        ret = {'name': member.name, 'type': self._use_type(member.type)}
129fb0bc835SMarkus Armbruster        if member.optional:
130fb0bc835SMarkus Armbruster            ret['default'] = None
131fb0bc835SMarkus Armbruster        return ret
132fb0bc835SMarkus Armbruster
133fb0bc835SMarkus Armbruster    def _gen_variants(self, tag_name, variants):
134fb0bc835SMarkus Armbruster        return {'tag': tag_name,
135fb0bc835SMarkus Armbruster                'variants': [self._gen_variant(v) for v in variants]}
136fb0bc835SMarkus Armbruster
137fb0bc835SMarkus Armbruster    def _gen_variant(self, variant):
138fb0bc835SMarkus Armbruster        return {'case': variant.name, 'type': self._use_type(variant.type)}
139fb0bc835SMarkus Armbruster
140fb0bc835SMarkus Armbruster    def visit_builtin_type(self, name, info, json_type):
141fb0bc835SMarkus Armbruster        self._gen_json(name, 'builtin', {'json-type': json_type})
142fb0bc835SMarkus Armbruster
143fb0bc835SMarkus Armbruster    def visit_enum_type(self, name, info, values, prefix):
144fb0bc835SMarkus Armbruster        self._gen_json(name, 'enum', {'values': values})
145fb0bc835SMarkus Armbruster
146fb0bc835SMarkus Armbruster    def visit_array_type(self, name, info, element_type):
147fb0bc835SMarkus Armbruster        element = self._use_type(element_type)
148fb0bc835SMarkus Armbruster        self._gen_json('[' + element + ']', 'array', {'element-type': element})
149fb0bc835SMarkus Armbruster
150fb0bc835SMarkus Armbruster    def visit_object_type_flat(self, name, info, members, variants):
151fb0bc835SMarkus Armbruster        obj = {'members': [self._gen_member(m) for m in members]}
152fb0bc835SMarkus Armbruster        if variants:
153fb0bc835SMarkus Armbruster            obj.update(self._gen_variants(variants.tag_member.name,
154fb0bc835SMarkus Armbruster                                          variants.variants))
155fb0bc835SMarkus Armbruster        self._gen_json(name, 'object', obj)
156fb0bc835SMarkus Armbruster
157fb0bc835SMarkus Armbruster    def visit_alternate_type(self, name, info, variants):
158fb0bc835SMarkus Armbruster        self._gen_json(name, 'alternate',
159fb0bc835SMarkus Armbruster                       {'members': [{'type': self._use_type(m.type)}
160fb0bc835SMarkus Armbruster                                    for m in variants.variants]})
161fb0bc835SMarkus Armbruster
162fb0bc835SMarkus Armbruster    def visit_command(self, name, info, arg_type, ret_type,
163fb0bc835SMarkus Armbruster                      gen, success_response, boxed):
164fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
165fb0bc835SMarkus Armbruster        ret_type = ret_type or self._schema.the_empty_object_type
166fb0bc835SMarkus Armbruster        self._gen_json(name, 'command',
167fb0bc835SMarkus Armbruster                       {'arg-type': self._use_type(arg_type),
168fb0bc835SMarkus Armbruster                        'ret-type': self._use_type(ret_type)})
169fb0bc835SMarkus Armbruster
170fb0bc835SMarkus Armbruster    def visit_event(self, name, info, arg_type, boxed):
171fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
172fb0bc835SMarkus Armbruster        self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
173fb0bc835SMarkus Armbruster
174fb0bc835SMarkus Armbruster
175fb0bc835SMarkus Armbrusterdef gen_introspect(schema, output_dir, prefix, opt_unmask):
176fb0bc835SMarkus Armbruster    vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
177fb0bc835SMarkus Armbruster    schema.visit(vis)
17871b3f045SMarkus Armbruster    vis.write(output_dir)
179