]>
Commit | Line | Data |
---|---|---|
5ddeec83 MA |
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 | """ | |
c17d9908 | 15 | |
e6a34cd7 JS |
16 | from .common import c_name, mcgen |
17 | from .gen import ( | |
18 | QAPIGenCCode, | |
19 | QAPISchemaModularCVisitor, | |
20 | build_params, | |
21 | ifcontext, | |
22 | ) | |
c17d9908 | 23 | |
e98859a9 | 24 | |
48825ca4 | 25 | def gen_command_decl(name, arg_type, boxed, ret_type): |
c17d9908 | 26 | return mcgen(''' |
03b4367a | 27 | %(c_type)s qmp_%(c_name)s(%(params)s); |
c17d9908 | 28 | ''', |
e98859a9 MA |
29 | c_type=(ret_type and ret_type.c_type()) or 'void', |
30 | c_name=c_name(name), | |
086ee7a6 | 31 | params=build_params(arg_type, boxed, 'Error **errp')) |
e98859a9 | 32 | |
c17d9908 | 33 | |
48825ca4 | 34 | def gen_call(name, arg_type, boxed, ret_type): |
e98859a9 MA |
35 | ret = '' |
36 | ||
37 | argstr = '' | |
48825ca4 | 38 | if boxed: |
675b214b | 39 | assert arg_type |
c818408e | 40 | argstr = '&arg, ' |
48825ca4 | 41 | elif arg_type: |
29f6bd15 | 42 | assert not arg_type.variants |
e98859a9 | 43 | for memb in arg_type.members: |
ee446028 | 44 | if memb.optional: |
386230a2 EB |
45 | argstr += 'arg.has_%s, ' % c_name(memb.name) |
46 | argstr += 'arg.%s, ' % c_name(memb.name) | |
e98859a9 MA |
47 | |
48 | lhs = '' | |
49 | if ret_type: | |
50 | lhs = 'retval = ' | |
51 | ||
c17d9908 | 52 | ret = mcgen(''' |
f1538019 | 53 | |
05372f70 | 54 | %(lhs)sqmp_%(c_name)s(%(args)s&err); |
cdd2b228 | 55 | error_propagate(errp, err); |
c17d9908 | 56 | ''', |
e98859a9 | 57 | c_name=c_name(name), args=argstr, lhs=lhs) |
c17d9908 | 58 | if ret_type: |
e02bca28 | 59 | ret += mcgen(''' |
fa274ed6 EB |
60 | if (err) { |
61 | goto out; | |
62 | } | |
e02bca28 | 63 | |
cdd2b228 | 64 | qmp_marshal_output_%(c_name)s(retval, ret, errp); |
c17d9908 | 65 | ''', |
56d92b00 | 66 | c_name=ret_type.c_name()) |
1f9a7a1a | 67 | return ret |
c17d9908 | 68 | |
e98859a9 | 69 | |
56d92b00 | 70 | def gen_marshal_output(ret_type): |
f1538019 | 71 | return mcgen(''' |
ee446028 | 72 | |
42c0dd12 JS |
73 | static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, |
74 | QObject **ret_out, Error **errp) | |
c17d9908 | 75 | { |
c17d9908 MR |
76 | Visitor *v; |
77 | ||
7d5e199a | 78 | v = qobject_output_visitor_new(ret_out); |
cdd2b228 | 79 | if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) { |
3b098d56 | 80 | visit_complete(v, ret_out); |
c17d9908 | 81 | } |
2c0ef9f4 EB |
82 | visit_free(v); |
83 | v = qapi_dealloc_visitor_new(); | |
51e72bc1 | 84 | visit_type_%(c_name)s(v, "unused", &ret_in, NULL); |
2c0ef9f4 | 85 | visit_free(v); |
c17d9908 MR |
86 | } |
87 | ''', | |
56d92b00 | 88 | c_type=ret_type.c_type(), c_name=ret_type.c_name()) |
c17d9908 | 89 | |
e98859a9 | 90 | |
086ee7a6 | 91 | def build_marshal_proto(name): |
c2613949 MA |
92 | return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' |
93 | % c_name(name)) | |
776574d6 | 94 | |
e98859a9 | 95 | |
f1538019 MA |
96 | def gen_marshal_decl(name): |
97 | return mcgen(''' | |
98 | %(proto)s; | |
99 | ''', | |
086ee7a6 | 100 | proto=build_marshal_proto(name)) |
f1538019 | 101 | |
776574d6 | 102 | |
48825ca4 | 103 | def gen_marshal(name, arg_type, boxed, ret_type): |
675b214b | 104 | have_args = boxed or (arg_type and not arg_type.is_empty()) |
a0067da1 | 105 | |
c17d9908 | 106 | ret = mcgen(''' |
ee446028 | 107 | |
f1538019 | 108 | %(proto)s |
c17d9908 | 109 | { |
c1ff0e6c | 110 | Error *err = NULL; |
cdd2b228 | 111 | bool ok = false; |
2061487b | 112 | Visitor *v; |
c17d9908 | 113 | ''', |
086ee7a6 | 114 | proto=build_marshal_proto(name)) |
c17d9908 | 115 | |
c1ff0e6c EB |
116 | if ret_type: |
117 | ret += mcgen(''' | |
118 | %(c_type)s retval; | |
119 | ''', | |
120 | c_type=ret_type.c_type()) | |
121 | ||
a0067da1 | 122 | if have_args: |
c1ff0e6c | 123 | ret += mcgen(''' |
c1ff0e6c | 124 | %(c_name)s arg = {0}; |
a0067da1 MAL |
125 | ''', |
126 | c_name=arg_type.c_name()) | |
a0067da1 MAL |
127 | |
128 | ret += mcgen(''' | |
2061487b | 129 | |
048abb7b | 130 | v = qobject_input_visitor_new(QOBJECT(args)); |
cdd2b228 | 131 | if (!visit_start_struct(v, NULL, NULL, 0, errp)) { |
ed841535 EB |
132 | goto out; |
133 | } | |
89bf68f9 MA |
134 | ''') |
135 | ||
136 | if have_args: | |
137 | ret += mcgen(''' | |
cdd2b228 MA |
138 | if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) { |
139 | ok = visit_check_struct(v, errp); | |
15c2f669 | 140 | } |
89bf68f9 MA |
141 | ''', |
142 | c_arg_type=arg_type.c_name()) | |
143 | else: | |
144 | ret += mcgen(''' | |
cdd2b228 | 145 | ok = visit_check_struct(v, errp); |
89bf68f9 MA |
146 | ''') |
147 | ||
148 | ret += mcgen(''' | |
1158bb2a | 149 | visit_end_struct(v, NULL); |
cdd2b228 | 150 | if (!ok) { |
c1ff0e6c EB |
151 | goto out; |
152 | } | |
89bf68f9 | 153 | ''') |
c1ff0e6c | 154 | |
48825ca4 | 155 | ret += gen_call(name, arg_type, boxed, ret_type) |
1f9a7a1a | 156 | |
a0067da1 | 157 | ret += mcgen(''' |
c17d9908 MR |
158 | |
159 | out: | |
a0067da1 | 160 | visit_free(v); |
1f9a7a1a | 161 | ''') |
a0067da1 | 162 | |
a0067da1 | 163 | ret += mcgen(''' |
2c0ef9f4 | 164 | v = qapi_dealloc_visitor_new(); |
ed841535 | 165 | visit_start_struct(v, NULL, NULL, 0, NULL); |
89bf68f9 MA |
166 | ''') |
167 | ||
168 | if have_args: | |
169 | ret += mcgen(''' | |
170 | visit_type_%(c_arg_type)s_members(v, &arg, NULL); | |
171 | ''', | |
172 | c_arg_type=arg_type.c_name()) | |
173 | ||
174 | ret += mcgen(''' | |
1158bb2a | 175 | visit_end_struct(v, NULL); |
2c0ef9f4 | 176 | visit_free(v); |
89bf68f9 | 177 | ''') |
a0067da1 | 178 | |
1f9a7a1a | 179 | ret += mcgen(''' |
485febc6 | 180 | } |
1f9a7a1a | 181 | ''') |
c17d9908 MR |
182 | return ret |
183 | ||
e98859a9 | 184 | |
04f22362 KW |
185 | def gen_register_command(name, success_response, allow_oob, allow_preconfig, |
186 | coroutine): | |
876c6751 PX |
187 | options = [] |
188 | ||
ee446028 | 189 | if not success_response: |
876c6751 PX |
190 | options += ['QCO_NO_SUCCESS_RESP'] |
191 | if allow_oob: | |
192 | options += ['QCO_ALLOW_OOB'] | |
d6fe3d02 IM |
193 | if allow_preconfig: |
194 | options += ['QCO_ALLOW_PRECONFIG'] | |
04f22362 KW |
195 | if coroutine: |
196 | options += ['QCO_COROUTINE'] | |
876c6751 PX |
197 | |
198 | if not options: | |
199 | options = ['QCO_NO_OPTIONS'] | |
200 | ||
ee446028 | 201 | ret = mcgen(''' |
c2613949 | 202 | qmp_register_command(cmds, "%(name)s", |
1527badb | 203 | qmp_marshal_%(c_name)s, %(opts)s); |
c17d9908 | 204 | ''', |
e98859a9 | 205 | name=name, c_name=c_name(name), |
ab2d8a75 | 206 | opts=" | ".join(options)) |
ee446028 MA |
207 | return ret |
208 | ||
e98859a9 | 209 | |
93b564c4 | 210 | def gen_registry(registry, prefix): |
c17d9908 | 211 | ret = mcgen(''' |
ee446028 | 212 | |
1527badb | 213 | void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) |
c17d9908 | 214 | { |
1527badb MA |
215 | QTAILQ_INIT(cmds); |
216 | ||
217 | ''', | |
218 | c_prefix=c_name(prefix, protect=False)) | |
1f9a7a1a MA |
219 | ret += registry |
220 | ret += mcgen(''' | |
c17d9908 | 221 | } |
1f9a7a1a | 222 | ''') |
c17d9908 MR |
223 | return ret |
224 | ||
ee446028 | 225 | |
252dc310 | 226 | class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): |
71b3f045 | 227 | |
93b564c4 | 228 | def __init__(self, prefix): |
2cae67bc MA |
229 | super().__init__( |
230 | prefix, 'qapi-commands', | |
3bef3aae | 231 | ' * Schema-defined QAPI/QMP commands', None, __doc__) |
dddee4d7 | 232 | self._regy = QAPIGenCCode(None) |
252dc310 MA |
233 | self._visited_ret_types = {} |
234 | ||
dcac6471 | 235 | def _begin_user_module(self, name): |
252dc310 | 236 | self._visited_ret_types[self._genc] = set() |
9af23989 MA |
237 | commands = self._module_basename('qapi-commands', name) |
238 | types = self._module_basename('qapi-types', name) | |
239 | visit = self._module_basename('qapi-visit', name) | |
71b3f045 | 240 | self._genc.add(mcgen(''' |
9167ebd9 | 241 | #include "qemu/osdep.h" |
4180978c | 242 | #include "qapi/visitor.h" |
452fcdbc | 243 | #include "qapi/qmp/qdict.h" |
b3db211f DB |
244 | #include "qapi/qobject-output-visitor.h" |
245 | #include "qapi/qobject-input-visitor.h" | |
4180978c | 246 | #include "qapi/dealloc-visitor.h" |
e688df6b | 247 | #include "qapi/error.h" |
9af23989 MA |
248 | #include "%(visit)s.h" |
249 | #include "%(commands)s.h" | |
4180978c MA |
250 | |
251 | ''', | |
9af23989 | 252 | commands=commands, visit=visit)) |
71b3f045 | 253 | self._genh.add(mcgen(''' |
9af23989 | 254 | #include "%(types)s.h" |
4180978c MA |
255 | |
256 | ''', | |
9af23989 | 257 | types=types)) |
71b3f045 MA |
258 | |
259 | def visit_end(self): | |
00ca24ff MA |
260 | self._add_system_module('init', ' * QAPI Commands initialization') |
261 | self._genh.add(mcgen(''' | |
262 | #include "qapi/qmp/dispatch.h" | |
263 | ||
252dc310 MA |
264 | void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); |
265 | ''', | |
8ec0e1a4 | 266 | c_prefix=c_name(self._prefix, protect=False))) |
00ca24ff MA |
267 | self._genc.preamble_add(mcgen(''' |
268 | #include "qemu/osdep.h" | |
269 | #include "%(prefix)sqapi-commands.h" | |
270 | #include "%(prefix)sqapi-init-commands.h" | |
271 | ''', | |
272 | prefix=self._prefix)) | |
273 | self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) | |
71b3f045 | 274 | |
7b3bc9e2 MA |
275 | def visit_command(self, name, info, ifcond, features, |
276 | arg_type, ret_type, gen, success_response, boxed, | |
04f22362 | 277 | allow_oob, allow_preconfig, coroutine): |
71b3f045 MA |
278 | if not gen: |
279 | return | |
1f7b9f31 MAL |
280 | # FIXME: If T is a user-defined type, the user is responsible |
281 | # for making this work, i.e. to make T's condition the | |
282 | # conjunction of the T-returning commands' conditions. If T | |
283 | # is a built-in type, this isn't possible: the | |
284 | # qmp_marshal_output_T() will be generated unconditionally. | |
252dc310 MA |
285 | if ret_type and ret_type not in self._visited_ret_types[self._genc]: |
286 | self._visited_ret_types[self._genc].add(ret_type) | |
1f7b9f31 MAL |
287 | with ifcontext(ret_type.ifcond, |
288 | self._genh, self._genc, self._regy): | |
289 | self._genc.add(gen_marshal_output(ret_type)) | |
290 | with ifcontext(ifcond, self._genh, self._genc, self._regy): | |
291 | self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) | |
292 | self._genh.add(gen_marshal_decl(name)) | |
293 | self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) | |
294 | self._regy.add(gen_register_command(name, success_response, | |
04f22362 KW |
295 | allow_oob, allow_preconfig, |
296 | coroutine)) | |
71b3f045 | 297 | |
26df4e7f | 298 | |
71b3f045 | 299 | def gen_commands(schema, output_dir, prefix): |
26df4e7f MA |
300 | vis = QAPISchemaGenCommandVisitor(prefix) |
301 | schema.visit(vis) | |
71b3f045 | 302 | vis.write(output_dir) |