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