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 *
14 from qapi
.gen
import QAPISchemaMonolithicCVisitor
15 from qapi
.schema
import (QAPISchemaArrayType
, QAPISchemaBuiltinType
,
19 def _make_tree(obj
, ifcond
, features
, extra
=None):
25 obj
['features'] = [(f
.name
, {'if': f
.ifcond
}) for f
in features
]
31 def _tree_to_qlit(obj
, level
=0, suppress_first_indent
=False):
34 return level
* 4 * ' '
36 if isinstance(obj
, tuple):
38 ifcond
= extra
.get('if')
39 comment
= extra
.get('comment')
42 ret
+= indent(level
) + '/* %s */\n' % comment
45 ret
+= _tree_to_qlit(ifobj
, level
)
47 ret
+= '\n' + gen_endif(ifcond
)
51 if not suppress_first_indent
:
55 elif isinstance(obj
, str):
56 ret
+= 'QLIT_QSTR(' + to_c_string(obj
) + ')'
57 elif isinstance(obj
, list):
58 elts
= [_tree_to_qlit(elt
, level
+ 1).strip('\n')
60 elts
.append(indent(level
+ 1) + "{}")
61 ret
+= 'QLIT_QLIST(((QLitObject[]) {\n'
62 ret
+= '\n'.join(elts
) + '\n'
63 ret
+= indent(level
) + '}))'
64 elif isinstance(obj
, dict):
66 for key
, value
in sorted(obj
.items()):
67 elts
.append(indent(level
+ 1) + '{ %s, %s }' %
69 _tree_to_qlit(value
, level
+ 1, True)))
70 elts
.append(indent(level
+ 1) + '{}')
71 ret
+= 'QLIT_QDICT(((QLitDictEntry[]) {\n'
72 ret
+= ',\n'.join(elts
) + '\n'
73 ret
+= indent(level
) + '}))'
74 elif isinstance(obj
, bool):
75 ret
+= 'QLIT_QBOOL(%s)' % ('true' if obj
else 'false')
77 assert False # not implemented
83 def to_c_string(string
):
84 return '"' + string
.replace('\\', r
'\\').replace('"', r
'\"') + '"'
87 class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor
):
89 def __init__(self
, prefix
, unmask
):
91 prefix
, 'qapi-introspect',
92 ' * QAPI/QMP schema introspection', __doc__
)
98 self
._genc
.add(mcgen('''
99 #include "qemu/osdep.h"
100 #include "%(prefix)sqapi-introspect.h"
105 def visit_begin(self
, schema
):
106 self
._schema
= schema
109 # visit the types that are actually used
110 for typ
in self
._used
_types
:
113 name
= c_name(self
._prefix
, protect
=False) + 'qmp_schema_qlit'
114 self
._genh
.add(mcgen('''
115 #include "qapi/qmp/qlit.h"
117 extern const QLitObject %(c_name)s;
119 c_name
=c_name(name
)))
120 self
._genc
.add(mcgen('''
121 const QLitObject %(c_name)s = %(c_string)s;
124 c_string
=_tree_to_qlit(self
._trees
)))
127 self
._used
_types
= []
130 def visit_needed(self
, entity
):
131 # Ignore types on first pass; visit_end() will pick up used types
132 return not isinstance(entity
, QAPISchemaType
)
134 def _name(self
, name
):
137 if name
not in self
._name
_map
:
138 self
._name
_map
[name
] = '%d' % len(self
._name
_map
)
139 return self
._name
_map
[name
]
141 def _use_type(self
, typ
):
142 # Map the various integer types to plain int
143 if typ
.json_type() == 'int':
144 typ
= self
._schema
.lookup_type('int')
145 elif (isinstance(typ
, QAPISchemaArrayType
) and
146 typ
.element_type
.json_type() == 'int'):
147 typ
= self
._schema
.lookup_type('intList')
148 # Add type to work queue if new
149 if typ
not in self
._used
_types
:
150 self
._used
_types
.append(typ
)
151 # Clients should examine commands and events, not types. Hide
152 # type names as integers to reduce the temptation. Also, it
153 # saves a few characters on the wire.
154 if isinstance(typ
, QAPISchemaBuiltinType
):
156 if isinstance(typ
, QAPISchemaArrayType
):
157 return '[' + self
._use
_type
(typ
.element_type
) + ']'
158 return self
._name
(typ
.name
)
160 def _gen_tree(self
, name
, mtype
, obj
, ifcond
, features
):
162 if mtype
not in ('command', 'event', 'builtin', 'array'):
164 # Output a comment to make it easy to map masked names
165 # back to the source when reading the generated output.
166 extra
= {'comment': '"%s" = %s' % (self
._name
(name
), name
)}
167 name
= self
._name
(name
)
169 obj
['meta-type'] = mtype
170 self
._trees
.append(_make_tree(obj
, ifcond
, features
, extra
))
172 def _gen_member(self
, member
):
173 obj
= {'name': member
.name
, 'type': self
._use
_type
(member
.type)}
175 obj
['default'] = None
176 return _make_tree(obj
, member
.ifcond
, member
.features
)
178 def _gen_variants(self
, tag_name
, variants
):
179 return {'tag': tag_name
,
180 'variants': [self
._gen
_variant
(v
) for v
in variants
]}
182 def _gen_variant(self
, variant
):
183 obj
= {'case': variant
.name
, 'type': self
._use
_type
(variant
.type)}
184 return _make_tree(obj
, variant
.ifcond
, None)
186 def visit_builtin_type(self
, name
, info
, json_type
):
187 self
._gen
_tree
(name
, 'builtin', {'json-type': json_type
}, [], None)
189 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
190 self
._gen
_tree
(name
, 'enum',
191 {'values': [_make_tree(m
.name
, m
.ifcond
, None)
195 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
196 element
= self
._use
_type
(element_type
)
197 self
._gen
_tree
('[' + element
+ ']', 'array', {'element-type': element
},
200 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
202 obj
= {'members': [self
._gen
_member
(m
) for m
in members
]}
204 obj
.update(self
._gen
_variants
(variants
.tag_member
.name
,
207 self
._gen
_tree
(name
, 'object', obj
, ifcond
, features
)
209 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
210 self
._gen
_tree
(name
, 'alternate',
212 _make_tree({'type': self
._use
_type
(m
.type)},
214 for m
in variants
.variants
]},
217 def visit_command(self
, name
, info
, ifcond
, features
,
218 arg_type
, ret_type
, gen
, success_response
, boxed
,
219 allow_oob
, allow_preconfig
):
220 arg_type
= arg_type
or self
._schema
.the_empty_object_type
221 ret_type
= ret_type
or self
._schema
.the_empty_object_type
222 obj
= {'arg-type': self
._use
_type
(arg_type
),
223 'ret-type': self
._use
_type
(ret_type
)}
225 obj
['allow-oob'] = allow_oob
226 self
._gen
_tree
(name
, 'command', obj
, ifcond
, features
)
228 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
229 arg_type
= arg_type
or self
._schema
.the_empty_object_type
230 self
._gen
_tree
(name
, 'event', {'arg-type': self
._use
_type
(arg_type
)},
234 def gen_introspect(schema
, output_dir
, prefix
, opt_unmask
):
235 vis
= QAPISchemaGenIntrospectVisitor(prefix
, opt_unmask
)
237 vis
.write(output_dir
)