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