qapi: enable use of g_autoptr with QAPI types
[qemu.git] / scripts / qapi / commands.py
1 """
2 QAPI command marshaller generator
3
4 Copyright IBM, Corp. 2011
5 Copyright (C) 2014-2018 Red Hat, Inc.
6
7 Authors:
8 Anthony Liguori <aliguori@us.ibm.com>
9 Michael Roth <mdroth@linux.vnet.ibm.com>
10 Markus Armbruster <armbru@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
16 from qapi.common import *
17 from qapi.gen import QAPIGenCCode, QAPISchemaModularCVisitor, ifcontext
18
19
20 def gen_command_decl(name, arg_type, boxed, ret_type):
21 return mcgen('''
22 %(c_type)s qmp_%(c_name)s(%(params)s);
23 ''',
24 c_type=(ret_type and ret_type.c_type()) or 'void',
25 c_name=c_name(name),
26 params=build_params(arg_type, boxed, 'Error **errp'))
27
28
29 def gen_call(name, arg_type, boxed, ret_type):
30 ret = ''
31
32 argstr = ''
33 if boxed:
34 assert arg_type
35 argstr = '&arg, '
36 elif arg_type:
37 assert not arg_type.variants
38 for memb in arg_type.members:
39 if memb.optional:
40 argstr += 'arg.has_%s, ' % c_name(memb.name)
41 argstr += 'arg.%s, ' % c_name(memb.name)
42
43 lhs = ''
44 if ret_type:
45 lhs = 'retval = '
46
47 ret = mcgen('''
48
49 %(lhs)sqmp_%(c_name)s(%(args)s&err);
50 error_propagate(errp, err);
51 ''',
52 c_name=c_name(name), args=argstr, lhs=lhs)
53 if ret_type:
54 ret += mcgen('''
55 if (err) {
56 goto out;
57 }
58
59 qmp_marshal_output_%(c_name)s(retval, ret, errp);
60 ''',
61 c_name=ret_type.c_name())
62 return ret
63
64
65 def gen_marshal_output(ret_type):
66 return mcgen('''
67
68 static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
69 {
70 Visitor *v;
71
72 v = qobject_output_visitor_new(ret_out);
73 if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
74 visit_complete(v, ret_out);
75 }
76 visit_free(v);
77 v = qapi_dealloc_visitor_new();
78 visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
79 visit_free(v);
80 }
81 ''',
82 c_type=ret_type.c_type(), c_name=ret_type.c_name())
83
84
85 def build_marshal_proto(name):
86 return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)'
87 % c_name(name))
88
89
90 def gen_marshal_decl(name):
91 return mcgen('''
92 %(proto)s;
93 ''',
94 proto=build_marshal_proto(name))
95
96
97 def gen_marshal(name, arg_type, boxed, ret_type):
98 have_args = boxed or (arg_type and not arg_type.is_empty())
99
100 ret = mcgen('''
101
102 %(proto)s
103 {
104 Error *err = NULL;
105 bool ok = false;
106 Visitor *v;
107 ''',
108 proto=build_marshal_proto(name))
109
110 if ret_type:
111 ret += mcgen('''
112 %(c_type)s retval;
113 ''',
114 c_type=ret_type.c_type())
115
116 if have_args:
117 ret += mcgen('''
118 %(c_name)s arg = {0};
119 ''',
120 c_name=arg_type.c_name())
121
122 ret += mcgen('''
123
124 v = qobject_input_visitor_new(QOBJECT(args));
125 if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
126 goto out;
127 }
128 ''')
129
130 if have_args:
131 ret += mcgen('''
132 if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) {
133 ok = visit_check_struct(v, errp);
134 }
135 ''',
136 c_arg_type=arg_type.c_name())
137 else:
138 ret += mcgen('''
139 ok = visit_check_struct(v, errp);
140 ''')
141
142 ret += mcgen('''
143 visit_end_struct(v, NULL);
144 if (!ok) {
145 goto out;
146 }
147 ''')
148
149 ret += gen_call(name, arg_type, boxed, ret_type)
150
151 ret += mcgen('''
152
153 out:
154 visit_free(v);
155 ''')
156
157 ret += mcgen('''
158 v = qapi_dealloc_visitor_new();
159 visit_start_struct(v, NULL, NULL, 0, NULL);
160 ''')
161
162 if have_args:
163 ret += mcgen('''
164 visit_type_%(c_arg_type)s_members(v, &arg, NULL);
165 ''',
166 c_arg_type=arg_type.c_name())
167
168 ret += mcgen('''
169 visit_end_struct(v, NULL);
170 visit_free(v);
171 ''')
172
173 ret += mcgen('''
174 }
175 ''')
176 return ret
177
178
179 def gen_register_command(name, success_response, allow_oob, allow_preconfig):
180 options = []
181
182 if not success_response:
183 options += ['QCO_NO_SUCCESS_RESP']
184 if allow_oob:
185 options += ['QCO_ALLOW_OOB']
186 if allow_preconfig:
187 options += ['QCO_ALLOW_PRECONFIG']
188
189 if not options:
190 options = ['QCO_NO_OPTIONS']
191
192 options = " | ".join(options)
193
194 ret = mcgen('''
195 qmp_register_command(cmds, "%(name)s",
196 qmp_marshal_%(c_name)s, %(opts)s);
197 ''',
198 name=name, c_name=c_name(name),
199 opts=options)
200 return ret
201
202
203 def gen_registry(registry, prefix):
204 ret = mcgen('''
205
206 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
207 {
208 QTAILQ_INIT(cmds);
209
210 ''',
211 c_prefix=c_name(prefix, protect=False))
212 ret += registry
213 ret += mcgen('''
214 }
215 ''')
216 return ret
217
218
219 class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
220
221 def __init__(self, prefix):
222 super().__init__(
223 prefix, 'qapi-commands',
224 ' * Schema-defined QAPI/QMP commands', None, __doc__)
225 self._regy = QAPIGenCCode(None)
226 self._visited_ret_types = {}
227
228 def _begin_user_module(self, name):
229 self._visited_ret_types[self._genc] = set()
230 commands = self._module_basename('qapi-commands', name)
231 types = self._module_basename('qapi-types', name)
232 visit = self._module_basename('qapi-visit', name)
233 self._genc.add(mcgen('''
234 #include "qemu/osdep.h"
235 #include "qapi/visitor.h"
236 #include "qapi/qmp/qdict.h"
237 #include "qapi/qobject-output-visitor.h"
238 #include "qapi/qobject-input-visitor.h"
239 #include "qapi/dealloc-visitor.h"
240 #include "qapi/error.h"
241 #include "%(visit)s.h"
242 #include "%(commands)s.h"
243
244 ''',
245 commands=commands, visit=visit))
246 self._genh.add(mcgen('''
247 #include "%(types)s.h"
248
249 ''',
250 types=types))
251
252 def visit_end(self):
253 self._add_system_module('init', ' * QAPI Commands initialization')
254 self._genh.add(mcgen('''
255 #include "qapi/qmp/dispatch.h"
256
257 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
258 ''',
259 c_prefix=c_name(self._prefix, protect=False)))
260 self._genc.preamble_add(mcgen('''
261 #include "qemu/osdep.h"
262 #include "%(prefix)sqapi-commands.h"
263 #include "%(prefix)sqapi-init-commands.h"
264 ''',
265 prefix=self._prefix))
266 self._genc.add(gen_registry(self._regy.get_content(), self._prefix))
267
268 def visit_command(self, name, info, ifcond, features,
269 arg_type, ret_type, gen, success_response, boxed,
270 allow_oob, allow_preconfig):
271 if not gen:
272 return
273 # FIXME: If T is a user-defined type, the user is responsible
274 # for making this work, i.e. to make T's condition the
275 # conjunction of the T-returning commands' conditions. If T
276 # is a built-in type, this isn't possible: the
277 # qmp_marshal_output_T() will be generated unconditionally.
278 if ret_type and ret_type not in self._visited_ret_types[self._genc]:
279 self._visited_ret_types[self._genc].add(ret_type)
280 with ifcontext(ret_type.ifcond,
281 self._genh, self._genc, self._regy):
282 self._genc.add(gen_marshal_output(ret_type))
283 with ifcontext(ifcond, self._genh, self._genc, self._regy):
284 self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
285 self._genh.add(gen_marshal_decl(name))
286 self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
287 self._regy.add(gen_register_command(name, success_response,
288 allow_oob, allow_preconfig))
289
290
291 def gen_commands(schema, output_dir, prefix):
292 vis = QAPISchemaGenCommandVisitor(prefix)
293 schema.visit(vis)
294 vis.write(output_dir)