]>
Commit | Line | Data |
---|---|---|
06d64c62 MR |
1 | # |
2 | # QAPI visitor generator | |
3 | # | |
4 | # Copyright IBM, Corp. 2011 | |
92b09bab | 5 | # Copyright (C) 2014-2016 Red Hat, Inc. |
06d64c62 MR |
6 | # |
7 | # Authors: | |
8 | # Anthony Liguori <aliguori@us.ibm.com> | |
9 | # Michael Roth <mdroth@linux.vnet.ibm.com> | |
297a3646 | 10 | # Markus Armbruster <armbru@redhat.com> |
06d64c62 | 11 | # |
678e48a2 MA |
12 | # This work is licensed under the terms of the GNU GPL, version 2. |
13 | # See the COPYING file in the top-level directory. | |
06d64c62 | 14 | |
06d64c62 | 15 | from qapi import * |
297a3646 | 16 | import re |
06d64c62 | 17 | |
e98859a9 | 18 | |
60f8546a MA |
19 | def gen_visit_decl(name, scalar=False): |
20 | c_type = c_name(name) + ' *' | |
21 | if not scalar: | |
22 | c_type += '*' | |
23 | return mcgen(''' | |
51e72bc1 | 24 | void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp); |
60f8546a MA |
25 | ''', |
26 | c_name=c_name(name), c_type=c_type) | |
27 | ||
28 | ||
4d91e911 | 29 | def gen_visit_members_decl(name): |
2208d649 | 30 | return mcgen(''' |
8c3f8e77 | 31 | |
4d91e911 | 32 | void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); |
8c3f8e77 | 33 | ''', |
4d91e911 | 34 | c_name=c_name(name)) |
d02cf377 EB |
35 | |
36 | ||
14f00c6c | 37 | def gen_visit_object_members(name, base, members, variants): |
4d91e911 | 38 | ret = mcgen(''' |
50f2bdc7 | 39 | |
4d91e911 | 40 | void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) |
50f2bdc7 KW |
41 | { |
42 | Error *err = NULL; | |
3a864e7c | 43 | |
50f2bdc7 | 44 | ''', |
4d91e911 | 45 | c_name=c_name(name)) |
d195325b | 46 | |
622f557f KW |
47 | if base: |
48 | ret += mcgen(''' | |
c81200b0 | 49 | visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err); |
622f557f | 50 | ''', |
ddf21908 | 51 | c_type=base.c_name()) |
1f353344 | 52 | ret += gen_err_check() |
622f557f | 53 | |
14f00c6c | 54 | ret += gen_visit_members(members, prefix='obj->') |
d195325b | 55 | |
9a5cd424 EB |
56 | if variants: |
57 | ret += mcgen(''' | |
65551903 | 58 | switch (obj->%(c_name)s) { |
9a5cd424 EB |
59 | ''', |
60 | c_name=c_name(variants.tag_member.name)) | |
61 | ||
62 | for var in variants.variants: | |
63 | # TODO ugly special case for simple union | |
64 | simple_union_type = var.simple_union_type() | |
65 | ret += mcgen(''' | |
66 | case %(case)s: | |
67 | ''', | |
68 | case=c_enum_const(variants.tag_member.type.name, | |
69 | var.name, | |
70 | variants.tag_member.type.prefix)) | |
71 | if simple_union_type: | |
72 | ret += mcgen(''' | |
65551903 | 73 | visit_type_%(c_type)s(v, "data", &obj->u.%(c_name)s, &err); |
9a5cd424 EB |
74 | ''', |
75 | c_type=simple_union_type.c_name(), | |
76 | c_name=c_name(var.name)) | |
77 | else: | |
78 | ret += mcgen(''' | |
c81200b0 | 79 | visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err); |
9a5cd424 EB |
80 | ''', |
81 | c_type=var.type.c_name(), | |
82 | c_name=c_name(var.name)) | |
83 | ret += mcgen(''' | |
84 | break; | |
85 | ''') | |
86 | ||
87 | ret += mcgen(''' | |
88 | default: | |
89 | abort(); | |
90 | } | |
91 | ''') | |
92 | ||
14f00c6c | 93 | # 'goto out' produced for base, by gen_visit_members() for each member, |
9a5cd424 EB |
94 | # and if variants were present |
95 | if base or members or variants: | |
297a3646 | 96 | ret += mcgen(''' |
50f2bdc7 | 97 | |
297a3646 MA |
98 | out: |
99 | ''') | |
100 | ret += mcgen(''' | |
50f2bdc7 KW |
101 | error_propagate(errp, err); |
102 | } | |
103 | ''') | |
d131c897 KW |
104 | return ret |
105 | ||
106 | ||
441cbac0 | 107 | def gen_visit_list(name, element_type): |
dd5ee2c2 EB |
108 | # FIXME: if *obj is NULL on entry, and the first visit_next_list() |
109 | # assigns to *obj, while a later one fails, we should clean up *obj | |
110 | # rather than leaving it non-NULL. As currently written, the caller must | |
111 | # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList. | |
06d64c62 MR |
112 | return mcgen(''' |
113 | ||
51e72bc1 | 114 | void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) |
06d64c62 | 115 | { |
d195325b | 116 | Error *err = NULL; |
297a3646 | 117 | GenericList *i, **prev; |
06d64c62 | 118 | |
f8b7f1a8 | 119 | visit_start_list(v, name, &err); |
297a3646 MA |
120 | if (err) { |
121 | goto out; | |
122 | } | |
123 | ||
124 | for (prev = (GenericList **)obj; | |
e65d89bf | 125 | !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL; |
297a3646 | 126 | prev = &i) { |
e98859a9 | 127 | %(c_name)s *native_i = (%(c_name)s *)i; |
51e72bc1 | 128 | visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err); |
06d64c62 | 129 | } |
297a3646 | 130 | |
08f9541d | 131 | visit_end_list(v); |
297a3646 MA |
132 | out: |
133 | error_propagate(errp, err); | |
06d64c62 MR |
134 | } |
135 | ''', | |
e98859a9 | 136 | c_name=c_name(name), c_elt_type=element_type.c_name()) |
06d64c62 | 137 | |
e98859a9 MA |
138 | |
139 | def gen_visit_enum(name): | |
06d64c62 MR |
140 | return mcgen(''' |
141 | ||
51e72bc1 | 142 | void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp) |
06d64c62 | 143 | { |
395a233f | 144 | int value = *obj; |
337283df | 145 | visit_type_enum(v, name, &value, %(c_name)s_lookup, errp); |
395a233f | 146 | *obj = value; |
06d64c62 MR |
147 | } |
148 | ''', | |
337283df | 149 | c_name=c_name(name)) |
06d64c62 | 150 | |
e98859a9 | 151 | |
441cbac0 | 152 | def gen_visit_alternate(name, variants): |
d00341af | 153 | promote_int = 'true' |
becceedc | 154 | ret = '' |
d00341af EB |
155 | for var in variants.variants: |
156 | if var.type.alternate_qtype() == 'QTYPE_QINT': | |
157 | promote_int = 'false' | |
158 | ||
becceedc | 159 | ret += mcgen(''' |
69dd62df | 160 | |
51e72bc1 | 161 | void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) |
69dd62df KW |
162 | { |
163 | Error *err = NULL; | |
164 | ||
dbf11922 EB |
165 | visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj), |
166 | %(promote_int)s, &err); | |
297a3646 MA |
167 | if (err) { |
168 | goto out; | |
169 | } | |
150d0564 | 170 | switch ((*obj)->type) { |
69dd62df | 171 | ''', |
becceedc | 172 | c_name=c_name(name), promote_int=promote_int) |
69dd62df | 173 | |
441cbac0 | 174 | for var in variants.variants: |
69dd62df | 175 | ret += mcgen(''' |
e98859a9 | 176 | case %(case)s: |
becceedc EB |
177 | ''', |
178 | case=var.type.alternate_qtype()) | |
179 | if isinstance(var.type, QAPISchemaObjectType): | |
180 | ret += mcgen(''' | |
181 | visit_start_struct(v, name, NULL, 0, &err); | |
182 | if (err) { | |
183 | break; | |
184 | } | |
c81200b0 | 185 | visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err); |
becceedc EB |
186 | error_propagate(errp, err); |
187 | err = NULL; | |
188 | visit_end_struct(v, &err); | |
189 | ''', | |
190 | c_type=var.type.c_name(), | |
191 | c_name=c_name(var.name)) | |
192 | else: | |
193 | ret += mcgen(''' | |
51e72bc1 | 194 | visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err); |
69dd62df | 195 | ''', |
becceedc EB |
196 | c_type=var.type.c_name(), |
197 | c_name=c_name(var.name)) | |
198 | ret += mcgen(''' | |
199 | break; | |
200 | ''') | |
69dd62df KW |
201 | |
202 | ret += mcgen(''' | |
297a3646 | 203 | default: |
0426d53c EB |
204 | error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
205 | "%(name)s"); | |
69dd62df | 206 | } |
dbf11922 | 207 | visit_end_alternate(v); |
297a3646 MA |
208 | out: |
209 | error_propagate(errp, err); | |
69dd62df | 210 | } |
0426d53c EB |
211 | ''', |
212 | name=name) | |
69dd62df KW |
213 | |
214 | return ret | |
215 | ||
e98859a9 | 216 | |
59d9e84c | 217 | def gen_visit_object(name, base, members, variants): |
14f00c6c | 218 | ret = gen_visit_object_members(name, base, members, variants) |
be3c7717 | 219 | |
59d9e84c | 220 | # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to |
14f00c6c | 221 | # *obj, but then visit_type_FOO_members() fails, we should clean up *obj |
59d9e84c MA |
222 | # rather than leaving it non-NULL. As currently written, the caller must |
223 | # call qapi_free_FOO() to avoid a memory leak of the partial FOO. | |
06d64c62 MR |
224 | ret += mcgen(''' |
225 | ||
51e72bc1 | 226 | void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) |
06d64c62 | 227 | { |
dc8fb6df PB |
228 | Error *err = NULL; |
229 | ||
337283df | 230 | visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err); |
297a3646 MA |
231 | if (err) { |
232 | goto out; | |
233 | } | |
e36c714e EB |
234 | if (!*obj) { |
235 | goto out_obj; | |
236 | } | |
c81200b0 | 237 | visit_type_%(c_name)s_members(v, *obj, &err); |
e36c714e EB |
238 | error_propagate(errp, err); |
239 | err = NULL; | |
9a5cd424 | 240 | out_obj: |
f8b7f1a8 | 241 | visit_end_struct(v, &err); |
297a3646 MA |
242 | out: |
243 | error_propagate(errp, err); | |
dc8fb6df | 244 | } |
9a5cd424 EB |
245 | ''', |
246 | c_name=c_name(name)) | |
dc8fb6df | 247 | |
06d64c62 MR |
248 | return ret |
249 | ||
e98859a9 | 250 | |
441cbac0 MA |
251 | class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): |
252 | def __init__(self): | |
253 | self.decl = None | |
254 | self.defn = None | |
255 | self._btin = None | |
256 | ||
257 | def visit_begin(self, schema): | |
258 | self.decl = '' | |
259 | self.defn = '' | |
260 | self._btin = guardstart('QAPI_VISIT_BUILTIN') | |
261 | ||
262 | def visit_end(self): | |
263 | # To avoid header dependency hell, we always generate | |
264 | # declarations for built-in types in our header files and | |
265 | # simply guard them. See also do_builtins (command line | |
266 | # option -b). | |
267 | self._btin += guardend('QAPI_VISIT_BUILTIN') | |
268 | self.decl = self._btin + self.decl | |
269 | self._btin = None | |
270 | ||
25a0d9c9 EB |
271 | def visit_needed(self, entity): |
272 | # Visit everything except implicit objects | |
49823c4b EB |
273 | return not (entity.is_implicit() and |
274 | isinstance(entity, QAPISchemaObjectType)) | |
25a0d9c9 | 275 | |
441cbac0 | 276 | def visit_enum_type(self, name, info, values, prefix): |
7264f5c5 EB |
277 | # Special case for our lone builtin enum type |
278 | # TODO use something cleaner than existence of info | |
279 | if not info: | |
280 | self._btin += gen_visit_decl(name, scalar=True) | |
281 | if do_builtins: | |
282 | self.defn += gen_visit_enum(name) | |
283 | else: | |
284 | self.decl += gen_visit_decl(name, scalar=True) | |
285 | self.defn += gen_visit_enum(name) | |
441cbac0 MA |
286 | |
287 | def visit_array_type(self, name, info, element_type): | |
288 | decl = gen_visit_decl(name) | |
289 | defn = gen_visit_list(name, element_type) | |
290 | if isinstance(element_type, QAPISchemaBuiltinType): | |
291 | self._btin += decl | |
292 | if do_builtins: | |
293 | self.defn += defn | |
294 | else: | |
295 | self.decl += decl | |
296 | self.defn += defn | |
297 | ||
298 | def visit_object_type(self, name, info, base, members, variants): | |
4d91e911 | 299 | self.decl += gen_visit_members_decl(name) |
25a0d9c9 | 300 | self.decl += gen_visit_decl(name) |
59d9e84c | 301 | self.defn += gen_visit_object(name, base, members, variants) |
441cbac0 MA |
302 | |
303 | def visit_alternate_type(self, name, info, variants): | |
304 | self.decl += gen_visit_decl(name) | |
305 | self.defn += gen_visit_alternate(name, variants) | |
306 | ||
307 | # If you link code generated from multiple schemata, you want only one | |
308 | # instance of the code for built-in types. Generate it only when | |
309 | # do_builtins, enabled by command line option -b. See also | |
310 | # QAPISchemaGenVisitVisitor.visit_end(). | |
7c946bc4 | 311 | do_builtins = False |
8d3bc517 | 312 | |
2114f5a9 MA |
313 | (input_file, output_dir, do_c, do_h, prefix, opts) = \ |
314 | parse_command_line("b", ["builtins"]) | |
315 | ||
06d64c62 | 316 | for o, a in opts: |
2114f5a9 | 317 | if o in ("-b", "--builtins"): |
7c946bc4 | 318 | do_builtins = True |
8d3bc517 | 319 | |
12f8e1b9 | 320 | c_comment = ''' |
06d64c62 MR |
321 | /* |
322 | * schema-defined QAPI visitor functions | |
323 | * | |
324 | * Copyright IBM, Corp. 2011 | |
325 | * | |
326 | * Authors: | |
327 | * Anthony Liguori <aliguori@us.ibm.com> | |
328 | * | |
329 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
330 | * See the COPYING.LIB file in the top-level directory. | |
331 | * | |
332 | */ | |
12f8e1b9 MA |
333 | ''' |
334 | h_comment = ''' | |
06d64c62 | 335 | /* |
297a3646 | 336 | * schema-defined QAPI visitor functions |
06d64c62 MR |
337 | * |
338 | * Copyright IBM, Corp. 2011 | |
339 | * | |
340 | * Authors: | |
341 | * Anthony Liguori <aliguori@us.ibm.com> | |
342 | * | |
343 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
344 | * See the COPYING.LIB file in the top-level directory. | |
345 | * | |
346 | */ | |
12f8e1b9 MA |
347 | ''' |
348 | ||
349 | (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, | |
350 | 'qapi-visit.c', 'qapi-visit.h', | |
351 | c_comment, h_comment) | |
06d64c62 | 352 | |
12f8e1b9 | 353 | fdef.write(mcgen(''' |
9167ebd9 | 354 | #include "qemu/osdep.h" |
12f8e1b9 MA |
355 | #include "qemu-common.h" |
356 | #include "%(prefix)sqapi-visit.h" | |
357 | ''', | |
e98859a9 | 358 | prefix=prefix)) |
06d64c62 | 359 | |
12f8e1b9 | 360 | fdecl.write(mcgen(''' |
7b1b5d19 | 361 | #include "qapi/visitor.h" |
0426d53c | 362 | #include "qapi/qmp/qerror.h" |
06d64c62 | 363 | #include "%(prefix)sqapi-types.h" |
7c946bc4 | 364 | |
06d64c62 | 365 | ''', |
12f8e1b9 | 366 | prefix=prefix)) |
06d64c62 | 367 | |
441cbac0 MA |
368 | schema = QAPISchema(input_file) |
369 | gen = QAPISchemaGenVisitVisitor() | |
370 | schema.visit(gen) | |
371 | fdef.write(gen.defn) | |
372 | fdecl.write(gen.decl) | |
06d64c62 | 373 | |
12f8e1b9 | 374 | close_output(fdef, fdecl) |