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