]>
Commit | Line | Data |
---|---|---|
06d64c62 MR |
1 | # |
2 | # QAPI visitor generator | |
3 | # | |
4 | # Copyright IBM, Corp. 2011 | |
6540e9f3 | 5 | # Copyright (C) 2014-2015 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 | |
d02cf377 EB |
18 | # visit_type_FOO_implicit() is emitted as needed; track if it has already |
19 | # been output. | |
8c07eddc | 20 | implicit_structs_seen = set() |
d02cf377 EB |
21 | |
22 | # visit_type_FOO_fields() is always emitted; track if a forward declaration | |
23 | # or implementation has already been output. | |
8c3f8e77 | 24 | struct_fields_seen = set() |
be3c7717 | 25 | |
e98859a9 | 26 | |
60f8546a MA |
27 | def gen_visit_decl(name, scalar=False): |
28 | c_type = c_name(name) + ' *' | |
29 | if not scalar: | |
30 | c_type += '*' | |
31 | return mcgen(''' | |
f8b7f1a8 | 32 | void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, Error **errp); |
60f8546a MA |
33 | ''', |
34 | c_name=c_name(name), c_type=c_type) | |
35 | ||
36 | ||
d02cf377 | 37 | def gen_visit_fields_decl(typ): |
8c3f8e77 | 38 | ret = '' |
e98859a9 | 39 | if typ.name not in struct_fields_seen: |
8c3f8e77 MA |
40 | ret += mcgen(''' |
41 | ||
f8b7f1a8 | 42 | static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp); |
8c3f8e77 | 43 | ''', |
e98859a9 | 44 | c_type=typ.c_name()) |
d02cf377 EB |
45 | struct_fields_seen.add(typ.name) |
46 | return ret | |
47 | ||
48 | ||
49 | def gen_visit_implicit_struct(typ): | |
50 | if typ in implicit_structs_seen: | |
51 | return '' | |
52 | implicit_structs_seen.add(typ) | |
53 | ||
54 | ret = gen_visit_fields_decl(typ) | |
8c3f8e77 MA |
55 | |
56 | ret += mcgen(''' | |
be3c7717 | 57 | |
f8b7f1a8 | 58 | static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp) |
be3c7717 MA |
59 | { |
60 | Error *err = NULL; | |
61 | ||
f8b7f1a8 | 62 | visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err); |
be3c7717 | 63 | if (!err) { |
f8b7f1a8 EB |
64 | visit_type_%(c_type)s_fields(v, obj, errp); |
65 | visit_end_implicit_struct(v, &err); | |
be3c7717 MA |
66 | } |
67 | error_propagate(errp, err); | |
68 | } | |
69 | ''', | |
e98859a9 | 70 | c_type=typ.c_name()) |
8c3f8e77 | 71 | return ret |
be3c7717 | 72 | |
e98859a9 MA |
73 | |
74 | def gen_visit_struct_fields(name, base, members): | |
8c3f8e77 MA |
75 | struct_fields_seen.add(name) |
76 | ||
d131c897 | 77 | ret = '' |
50f2bdc7 | 78 | |
be3c7717 | 79 | if base: |
e98859a9 | 80 | ret += gen_visit_implicit_struct(base) |
be3c7717 | 81 | |
50f2bdc7 KW |
82 | ret += mcgen(''' |
83 | ||
f8b7f1a8 | 84 | static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp) |
50f2bdc7 KW |
85 | { |
86 | Error *err = NULL; | |
3a864e7c | 87 | |
50f2bdc7 | 88 | ''', |
e98859a9 | 89 | c_name=c_name(name)) |
d195325b | 90 | |
622f557f KW |
91 | if base: |
92 | ret += mcgen(''' | |
05372f70 | 93 | visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err); |
622f557f | 94 | ''', |
e98859a9 | 95 | c_type=base.c_name(), c_name=c_name('base')) |
1f353344 | 96 | ret += gen_err_check() |
622f557f | 97 | |
82ca8e46 | 98 | ret += gen_visit_fields(members, prefix='(*obj)->') |
d195325b | 99 | |
f9e6102b EB |
100 | # 'goto out' produced for base, and by gen_visit_fields() for each member |
101 | if base or members: | |
297a3646 | 102 | ret += mcgen(''' |
50f2bdc7 | 103 | |
297a3646 MA |
104 | out: |
105 | ''') | |
106 | ret += mcgen(''' | |
50f2bdc7 KW |
107 | error_propagate(errp, err); |
108 | } | |
109 | ''') | |
d131c897 KW |
110 | return ret |
111 | ||
112 | ||
60f8546a MA |
113 | def gen_visit_struct(name, base, members): |
114 | ret = gen_visit_struct_fields(name, base, members) | |
115 | ||
2f52e205 EB |
116 | # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to |
117 | # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj | |
118 | # rather than leaving it non-NULL. As currently written, the caller must | |
119 | # call qapi_free_FOO() to avoid a memory leak of the partial FOO. | |
60f8546a MA |
120 | ret += mcgen(''' |
121 | ||
f8b7f1a8 | 122 | void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp) |
60f8546a | 123 | { |
297a3646 MA |
124 | Error *err = NULL; |
125 | ||
f8b7f1a8 | 126 | visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); |
297a3646 MA |
127 | if (!err) { |
128 | if (*obj) { | |
f8b7f1a8 | 129 | visit_type_%(c_name)s_fields(v, obj, errp); |
297a3646 | 130 | } |
f8b7f1a8 | 131 | visit_end_struct(v, &err); |
50f2bdc7 | 132 | } |
297a3646 | 133 | error_propagate(errp, err); |
60f8546a | 134 | } |
50f2bdc7 | 135 | ''', |
60f8546a | 136 | name=name, c_name=c_name(name)) |
06d64c62 | 137 | |
06d64c62 MR |
138 | return ret |
139 | ||
e98859a9 | 140 | |
441cbac0 | 141 | def gen_visit_list(name, element_type): |
06d64c62 MR |
142 | return mcgen(''' |
143 | ||
f8b7f1a8 | 144 | void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp) |
06d64c62 | 145 | { |
d195325b | 146 | Error *err = NULL; |
297a3646 | 147 | GenericList *i, **prev; |
06d64c62 | 148 | |
f8b7f1a8 | 149 | visit_start_list(v, name, &err); |
297a3646 MA |
150 | if (err) { |
151 | goto out; | |
152 | } | |
153 | ||
154 | for (prev = (GenericList **)obj; | |
f8b7f1a8 | 155 | !err && (i = visit_next_list(v, prev, &err)) != NULL; |
297a3646 | 156 | prev = &i) { |
e98859a9 | 157 | %(c_name)s *native_i = (%(c_name)s *)i; |
f8b7f1a8 | 158 | visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err); |
06d64c62 | 159 | } |
297a3646 MA |
160 | |
161 | error_propagate(errp, err); | |
162 | err = NULL; | |
f8b7f1a8 | 163 | visit_end_list(v, &err); |
297a3646 MA |
164 | out: |
165 | error_propagate(errp, err); | |
06d64c62 MR |
166 | } |
167 | ''', | |
e98859a9 | 168 | c_name=c_name(name), c_elt_type=element_type.c_name()) |
06d64c62 | 169 | |
e98859a9 MA |
170 | |
171 | def gen_visit_enum(name): | |
06d64c62 MR |
172 | return mcgen(''' |
173 | ||
f8b7f1a8 | 174 | void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp) |
06d64c62 | 175 | { |
f8b7f1a8 | 176 | visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp); |
06d64c62 MR |
177 | } |
178 | ''', | |
40b3adec | 179 | c_name=c_name(name), name=name) |
06d64c62 | 180 | |
e98859a9 | 181 | |
441cbac0 | 182 | def gen_visit_alternate(name, variants): |
69dd62df KW |
183 | ret = mcgen(''' |
184 | ||
f8b7f1a8 | 185 | void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp) |
69dd62df KW |
186 | { |
187 | Error *err = NULL; | |
188 | ||
f8b7f1a8 | 189 | visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err); |
297a3646 MA |
190 | if (err) { |
191 | goto out; | |
192 | } | |
f8b7f1a8 | 193 | visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err); |
297a3646 | 194 | if (err) { |
f782399c | 195 | goto out_obj; |
297a3646 MA |
196 | } |
197 | switch ((*obj)->kind) { | |
69dd62df | 198 | ''', |
e98859a9 | 199 | c_name=c_name(name)) |
69dd62df | 200 | |
441cbac0 | 201 | for var in variants.variants: |
69dd62df | 202 | ret += mcgen(''' |
e98859a9 | 203 | case %(case)s: |
f8b7f1a8 | 204 | visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err); |
297a3646 | 205 | break; |
69dd62df | 206 | ''', |
e98859a9 MA |
207 | case=c_enum_const(variants.tag_member.type.name, |
208 | var.name), | |
209 | c_type=var.type.c_name(), | |
210 | c_name=c_name(var.name)) | |
69dd62df KW |
211 | |
212 | ret += mcgen(''' | |
297a3646 MA |
213 | default: |
214 | abort(); | |
69dd62df | 215 | } |
f782399c | 216 | out_obj: |
297a3646 MA |
217 | error_propagate(errp, err); |
218 | err = NULL; | |
f8b7f1a8 | 219 | visit_end_implicit_struct(v, &err); |
297a3646 MA |
220 | out: |
221 | error_propagate(errp, err); | |
69dd62df KW |
222 | } |
223 | ''') | |
224 | ||
225 | return ret | |
226 | ||
e98859a9 | 227 | |
441cbac0 MA |
228 | def gen_visit_union(name, base, variants): |
229 | ret = '' | |
06d64c62 | 230 | |
50f2bdc7 | 231 | if base: |
441cbac0 | 232 | members = [m for m in base.members if m != variants.tag_member] |
e98859a9 | 233 | ret += gen_visit_struct_fields(name, None, members) |
50f2bdc7 | 234 | |
441cbac0 MA |
235 | for var in variants.variants: |
236 | # Ugly special case for simple union TODO get rid of it | |
237 | if not var.simple_union_type(): | |
e98859a9 | 238 | ret += gen_visit_implicit_struct(var.type) |
be3c7717 | 239 | |
06d64c62 MR |
240 | ret += mcgen(''' |
241 | ||
f8b7f1a8 | 242 | void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp) |
06d64c62 | 243 | { |
dc8fb6df PB |
244 | Error *err = NULL; |
245 | ||
f8b7f1a8 | 246 | visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); |
297a3646 MA |
247 | if (err) { |
248 | goto out; | |
249 | } | |
e36c714e EB |
250 | if (!*obj) { |
251 | goto out_obj; | |
252 | } | |
06d64c62 | 253 | ''', |
40b3adec | 254 | c_name=c_name(name), name=name) |
06d64c62 | 255 | |
0aef92b9 | 256 | if base: |
50f2bdc7 | 257 | ret += mcgen(''' |
e36c714e | 258 | visit_type_%(c_name)s_fields(v, obj, &err); |
50f2bdc7 | 259 | ''', |
e98859a9 | 260 | c_name=c_name(name)) |
1f353344 | 261 | ret += gen_err_check(label='out_obj') |
0aef92b9 | 262 | |
e98859a9 | 263 | tag_key = variants.tag_member.name |
441cbac0 MA |
264 | if not variants.tag_name: |
265 | # we pointlessly use a different key for simple unions | |
e98859a9 | 266 | tag_key = 'type' |
0aef92b9 | 267 | ret += mcgen(''' |
e36c714e EB |
268 | visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err); |
269 | if (err) { | |
270 | goto out_obj; | |
271 | } | |
272 | if (!visit_start_union(v, !!(*obj)->data, &err) || err) { | |
273 | goto out_obj; | |
274 | } | |
275 | switch ((*obj)->%(c_name)s) { | |
0aef92b9 | 276 | ''', |
e98859a9 | 277 | c_type=variants.tag_member.type.c_name(), |
441cbac0 MA |
278 | # TODO ugly special case for simple union |
279 | # Use same tag name in C as on the wire to get rid of | |
280 | # it, then: c_name=c_name(variants.tag_member.name) | |
281 | c_name=c_name(variants.tag_name or 'kind'), | |
e98859a9 | 282 | name=tag_key) |
0aef92b9 | 283 | |
441cbac0 MA |
284 | for var in variants.variants: |
285 | # TODO ugly special case for simple union | |
286 | simple_union_type = var.simple_union_type() | |
e98859a9 | 287 | ret += mcgen(''' |
e36c714e | 288 | case %(case)s: |
e98859a9 MA |
289 | ''', |
290 | case=c_enum_const(variants.tag_member.type.name, | |
291 | var.name)) | |
441cbac0 | 292 | if simple_union_type: |
e98859a9 | 293 | ret += mcgen(''' |
e36c714e | 294 | visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err); |
e98859a9 MA |
295 | ''', |
296 | c_type=simple_union_type.c_name(), | |
297 | c_name=c_name(var.name)) | |
50f2bdc7 | 298 | else: |
e98859a9 | 299 | ret += mcgen(''' |
e36c714e | 300 | visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err); |
e98859a9 MA |
301 | ''', |
302 | c_type=var.type.c_name(), | |
303 | c_name=c_name(var.name)) | |
dc8fb6df | 304 | ret += mcgen(''' |
e36c714e | 305 | break; |
e98859a9 | 306 | ''') |
dc8fb6df PB |
307 | |
308 | ret += mcgen(''' | |
e36c714e EB |
309 | default: |
310 | abort(); | |
468866b8 | 311 | } |
e36c714e EB |
312 | out_obj: |
313 | error_propagate(errp, err); | |
314 | err = NULL; | |
d08ac81a EB |
315 | if (*obj) { |
316 | visit_end_union(v, !!(*obj)->data, &err); | |
317 | } | |
e36c714e EB |
318 | error_propagate(errp, err); |
319 | err = NULL; | |
f8b7f1a8 | 320 | visit_end_struct(v, &err); |
297a3646 MA |
321 | out: |
322 | error_propagate(errp, err); | |
dc8fb6df PB |
323 | } |
324 | ''') | |
325 | ||
06d64c62 MR |
326 | return ret |
327 | ||
e98859a9 | 328 | |
441cbac0 MA |
329 | class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): |
330 | def __init__(self): | |
331 | self.decl = None | |
332 | self.defn = None | |
333 | self._btin = None | |
334 | ||
335 | def visit_begin(self, schema): | |
336 | self.decl = '' | |
337 | self.defn = '' | |
338 | self._btin = guardstart('QAPI_VISIT_BUILTIN') | |
339 | ||
340 | def visit_end(self): | |
341 | # To avoid header dependency hell, we always generate | |
342 | # declarations for built-in types in our header files and | |
343 | # simply guard them. See also do_builtins (command line | |
344 | # option -b). | |
345 | self._btin += guardend('QAPI_VISIT_BUILTIN') | |
346 | self.decl = self._btin + self.decl | |
347 | self._btin = None | |
348 | ||
25a0d9c9 EB |
349 | def visit_needed(self, entity): |
350 | # Visit everything except implicit objects | |
49823c4b EB |
351 | return not (entity.is_implicit() and |
352 | isinstance(entity, QAPISchemaObjectType)) | |
25a0d9c9 | 353 | |
441cbac0 MA |
354 | def visit_enum_type(self, name, info, values, prefix): |
355 | self.decl += gen_visit_decl(name, scalar=True) | |
e98859a9 | 356 | self.defn += gen_visit_enum(name) |
441cbac0 MA |
357 | |
358 | def visit_array_type(self, name, info, element_type): | |
359 | decl = gen_visit_decl(name) | |
360 | defn = gen_visit_list(name, element_type) | |
361 | if isinstance(element_type, QAPISchemaBuiltinType): | |
362 | self._btin += decl | |
363 | if do_builtins: | |
364 | self.defn += defn | |
365 | else: | |
366 | self.decl += decl | |
367 | self.defn += defn | |
368 | ||
369 | def visit_object_type(self, name, info, base, members, variants): | |
25a0d9c9 EB |
370 | self.decl += gen_visit_decl(name) |
371 | if variants: | |
372 | assert not members # not implemented | |
373 | self.defn += gen_visit_union(name, base, variants) | |
374 | else: | |
375 | self.defn += gen_visit_struct(name, base, members) | |
441cbac0 MA |
376 | |
377 | def visit_alternate_type(self, name, info, variants): | |
378 | self.decl += gen_visit_decl(name) | |
379 | self.defn += gen_visit_alternate(name, variants) | |
380 | ||
381 | # If you link code generated from multiple schemata, you want only one | |
382 | # instance of the code for built-in types. Generate it only when | |
383 | # do_builtins, enabled by command line option -b. See also | |
384 | # QAPISchemaGenVisitVisitor.visit_end(). | |
7c946bc4 | 385 | do_builtins = False |
8d3bc517 | 386 | |
2114f5a9 MA |
387 | (input_file, output_dir, do_c, do_h, prefix, opts) = \ |
388 | parse_command_line("b", ["builtins"]) | |
389 | ||
06d64c62 | 390 | for o, a in opts: |
2114f5a9 | 391 | if o in ("-b", "--builtins"): |
7c946bc4 | 392 | do_builtins = True |
8d3bc517 | 393 | |
12f8e1b9 | 394 | c_comment = ''' |
06d64c62 MR |
395 | /* |
396 | * schema-defined QAPI visitor functions | |
397 | * | |
398 | * Copyright IBM, Corp. 2011 | |
399 | * | |
400 | * Authors: | |
401 | * Anthony Liguori <aliguori@us.ibm.com> | |
402 | * | |
403 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
404 | * See the COPYING.LIB file in the top-level directory. | |
405 | * | |
406 | */ | |
12f8e1b9 MA |
407 | ''' |
408 | h_comment = ''' | |
06d64c62 | 409 | /* |
297a3646 | 410 | * schema-defined QAPI visitor functions |
06d64c62 MR |
411 | * |
412 | * Copyright IBM, Corp. 2011 | |
413 | * | |
414 | * Authors: | |
415 | * Anthony Liguori <aliguori@us.ibm.com> | |
416 | * | |
417 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
418 | * See the COPYING.LIB file in the top-level directory. | |
419 | * | |
420 | */ | |
12f8e1b9 MA |
421 | ''' |
422 | ||
423 | (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, | |
424 | 'qapi-visit.c', 'qapi-visit.h', | |
425 | c_comment, h_comment) | |
06d64c62 | 426 | |
12f8e1b9 MA |
427 | fdef.write(mcgen(''' |
428 | #include "qemu-common.h" | |
429 | #include "%(prefix)sqapi-visit.h" | |
430 | ''', | |
e98859a9 | 431 | prefix=prefix)) |
06d64c62 | 432 | |
12f8e1b9 | 433 | fdecl.write(mcgen(''' |
7b1b5d19 | 434 | #include "qapi/visitor.h" |
06d64c62 | 435 | #include "%(prefix)sqapi-types.h" |
7c946bc4 | 436 | |
06d64c62 | 437 | ''', |
12f8e1b9 | 438 | prefix=prefix)) |
06d64c62 | 439 | |
441cbac0 MA |
440 | schema = QAPISchema(input_file) |
441 | gen = QAPISchemaGenVisitVisitor() | |
442 | schema.visit(gen) | |
443 | fdef.write(gen.defn) | |
444 | fdecl.write(gen.decl) | |
06d64c62 | 445 | |
12f8e1b9 | 446 | close_output(fdef, fdecl) |