2 QAPI introspection generator
4 Copyright (C) 2015-2018 Red Hat, Inc.
7 Markus Armbruster <armbru@redhat.com>
9 This work is licensed under the terms of the GNU GPL, version 2.
10 See the COPYING file in the top-level directory.
13 from qapi
.common
import *
16 def to_qlit(obj
, level
=0, suppress_first_indent
=False):
19 return level
* 4 * ' '
22 if not suppress_first_indent
:
26 elif isinstance(obj
, str):
27 ret
+= 'QLIT_QSTR(' + to_c_string(obj
) + ')'
28 elif isinstance(obj
, list):
29 elts
= [to_qlit(elt
, level
+ 1)
31 elts
.append(indent(level
+ 1) + "{}")
32 ret
+= 'QLIT_QLIST(((QLitObject[]) {\n'
33 ret
+= ',\n'.join(elts
) + '\n'
34 ret
+= indent(level
) + '}))'
35 elif isinstance(obj
, dict):
37 for key
, value
in sorted(obj
.items()):
38 elts
.append(indent(level
+ 1) + '{ %s, %s }' %
39 (to_c_string(key
), to_qlit(value
, level
+ 1, True)))
40 elts
.append(indent(level
+ 1) + '{}')
41 ret
+= 'QLIT_QDICT(((QLitDictEntry[]) {\n'
42 ret
+= ',\n'.join(elts
) + '\n'
43 ret
+= indent(level
) + '}))'
44 elif isinstance(obj
, bool):
45 ret
+= 'QLIT_QBOOL(%s)' % ('true' if obj
else 'false')
47 assert False # not implemented
51 def to_c_string(string
):
52 return '"' + string
.replace('\\', r
'\\').replace('"', r
'\"') + '"'
55 class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor
):
57 def __init__(self
, prefix
, unmask
):
58 QAPISchemaMonolithicCVisitor
.__init
__(
59 self
, prefix
, 'qapi-introspect',
60 ' * QAPI/QMP schema introspection', __doc__
)
66 self
._genc
.add(mcgen('''
67 #include "qemu/osdep.h"
68 #include "%(prefix)sqapi-introspect.h"
73 def visit_begin(self
, schema
):
77 # visit the types that are actually used
80 for typ
in self
._used
_types
:
83 # TODO can generate awfully long lines
84 qlits
.extend(self
._qlits
)
85 name
= c_name(self
._prefix
, protect
=False) + 'qmp_schema_qlit'
86 self
._genh
.add(mcgen('''
87 #include "qapi/qmp/qlit.h"
89 extern const QLitObject %(c_name)s;
92 self
._genc
.add(mcgen('''
93 const QLitObject %(c_name)s = %(c_string)s;
96 c_string
=to_qlit(qlits
)))
102 def visit_needed(self
, entity
):
103 # Ignore types on first pass; visit_end() will pick up used types
104 return not isinstance(entity
, QAPISchemaType
)
106 def _name(self
, name
):
109 if name
not in self
._name
_map
:
110 self
._name
_map
[name
] = '%d' % len(self
._name
_map
)
111 return self
._name
_map
[name
]
113 def _use_type(self
, typ
):
114 # Map the various integer types to plain int
115 if typ
.json_type() == 'int':
116 typ
= self
._schema
.lookup_type('int')
117 elif (isinstance(typ
, QAPISchemaArrayType
) and
118 typ
.element_type
.json_type() == 'int'):
119 typ
= self
._schema
.lookup_type('intList')
120 # Add type to work queue if new
121 if typ
not in self
._used
_types
:
122 self
._used
_types
.append(typ
)
123 # Clients should examine commands and events, not types. Hide
124 # type names to reduce the temptation. Also saves a few
126 if isinstance(typ
, QAPISchemaBuiltinType
):
128 if isinstance(typ
, QAPISchemaArrayType
):
129 return '[' + self
._use
_type
(typ
.element_type
) + ']'
130 return self
._name
(typ
.name
)
132 def _gen_qlit(self
, name
, mtype
, obj
):
133 if mtype
not in ('command', 'event', 'builtin', 'array'):
134 name
= self
._name
(name
)
136 obj
['meta-type'] = mtype
137 self
._qlits
.append(obj
)
139 def _gen_member(self
, member
):
140 ret
= {'name': member
.name
, 'type': self
._use
_type
(member
.type)}
142 ret
['default'] = None
145 def _gen_variants(self
, tag_name
, variants
):
146 return {'tag': tag_name
,
147 'variants': [self
._gen
_variant
(v
) for v
in variants
]}
149 def _gen_variant(self
, variant
):
150 return {'case': variant
.name
, 'type': self
._use
_type
(variant
.type)}
152 def visit_builtin_type(self
, name
, info
, json_type
):
153 self
._gen
_qlit
(name
, 'builtin', {'json-type': json_type
})
155 def visit_enum_type(self
, name
, info
, values
, prefix
):
156 self
._gen
_qlit
(name
, 'enum', {'values': values
})
158 def visit_array_type(self
, name
, info
, element_type
):
159 element
= self
._use
_type
(element_type
)
160 self
._gen
_qlit
('[' + element
+ ']', 'array', {'element-type': element
})
162 def visit_object_type_flat(self
, name
, info
, members
, variants
):
163 obj
= {'members': [self
._gen
_member
(m
) for m
in members
]}
165 obj
.update(self
._gen
_variants
(variants
.tag_member
.name
,
167 self
._gen
_qlit
(name
, 'object', obj
)
169 def visit_alternate_type(self
, name
, info
, variants
):
170 self
._gen
_qlit
(name
, 'alternate',
171 {'members': [{'type': self
._use
_type
(m
.type)}
172 for m
in variants
.variants
]})
174 def visit_command(self
, name
, info
, arg_type
, ret_type
,
175 gen
, success_response
, boxed
, allow_oob
):
176 arg_type
= arg_type
or self
._schema
.the_empty_object_type
177 ret_type
= ret_type
or self
._schema
.the_empty_object_type
178 self
._gen
_qlit
(name
, 'command',
179 {'arg-type': self
._use
_type
(arg_type
),
180 'ret-type': self
._use
_type
(ret_type
),
181 'allow-oob': allow_oob
})
183 def visit_event(self
, name
, info
, arg_type
, boxed
):
184 arg_type
= arg_type
or self
._schema
.the_empty_object_type
185 self
._gen
_qlit
(name
, 'event', {'arg-type': self
._use
_type
(arg_type
)})
188 def gen_introspect(schema
, output_dir
, prefix
, opt_unmask
):
189 vis
= QAPISchemaGenIntrospectVisitor(prefix
, opt_unmask
)
191 vis
.write(output_dir
)