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