qapi-visit.py 11.0 KB
Newer Older
1 2 3 4
#
# QAPI visitor generator
#
# Copyright IBM, Corp. 2011
5
# Copyright (C) 2014-2016 Red Hat, Inc.
6 7 8 9
#
# Authors:
#  Anthony Liguori <aliguori@us.ibm.com>
#  Michael Roth    <mdroth@linux.vnet.ibm.com>
10
#  Markus Armbruster <armbru@redhat.com>
11
#
12 13
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
14 15

from qapi import *
16
import re
17

18 19
# visit_type_FOO_implicit() is emitted as needed; track if it has already
# been output.
20
implicit_structs_seen = set()
21 22 23

# visit_type_FOO_fields() is always emitted; track if a forward declaration
# or implementation has already been output.
24
struct_fields_seen = set()
25

26

27 28 29 30 31
def gen_visit_decl(name, scalar=False):
    c_type = c_name(name) + ' *'
    if not scalar:
        c_type += '*'
    return mcgen('''
32
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
33 34 35 36
''',
                 c_name=c_name(name), c_type=c_type)


37
def gen_visit_fields_decl(typ):
38
    ret = ''
39
    if typ.name not in struct_fields_seen:
40 41
        ret += mcgen('''

42
static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
43
''',
44
                     c_type=typ.c_name())
45 46 47 48 49 50 51 52 53 54
        struct_fields_seen.add(typ.name)
    return ret


def gen_visit_implicit_struct(typ):
    if typ in implicit_structs_seen:
        return ''
    implicit_structs_seen.add(typ)

    ret = gen_visit_fields_decl(typ)
55 56

    ret += mcgen('''
57

58
static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
59 60 61
{
    Error *err = NULL;

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);
66 67 68 69
    }
    error_propagate(errp, err);
}
''',
70
                 c_type=typ.c_name())
71
    return ret
72

73

74
def gen_visit_struct_fields(name, base, members, variants):
75
    ret = ''
76

77
    if base:
E
Eric Blake 已提交
78
        ret += gen_visit_fields_decl(base)
79 80 81 82 83
    if variants:
        for var in variants.variants:
            # Ugly special case for simple union TODO get rid of it
            if not var.simple_union_type():
                ret += gen_visit_implicit_struct(var.type)
84

E
Eric Blake 已提交
85
    struct_fields_seen.add(name)
86 87
    ret += mcgen('''

88
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
89 90
{
    Error *err = NULL;
M
Markus Armbruster 已提交
91

92
''',
93
                 c_name=c_name(name))
P
Paolo Bonzini 已提交
94

95 96
    if base:
        ret += mcgen('''
E
Eric Blake 已提交
97
    visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
98
''',
E
Eric Blake 已提交
99
                     c_type=base.c_name())
E
Eric Blake 已提交
100
        ret += gen_err_check()
101

E
Eric Blake 已提交
102
    ret += gen_visit_fields(members, prefix='(*obj)->')
P
Paolo Bonzini 已提交
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    if variants:
        ret += mcgen('''
    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
        goto out;
    }
    switch ((*obj)->%(c_name)s) {
''',
                     c_name=c_name(variants.tag_member.name))

        for var in variants.variants:
            # TODO ugly special case for simple union
            simple_union_type = var.simple_union_type()
            ret += mcgen('''
    case %(case)s:
''',
                         case=c_enum_const(variants.tag_member.type.name,
                                           var.name,
                                           variants.tag_member.type.prefix))
            if simple_union_type:
                ret += mcgen('''
        visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
''',
                             c_type=simple_union_type.c_name(),
                             c_name=c_name(var.name))
            else:
                ret += mcgen('''
        visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
''',
                             c_type=var.type.c_name(),
                             c_name=c_name(var.name))
            ret += mcgen('''
        break;
''')

        ret += mcgen('''
    default:
        abort();
    }
''')

    # 'goto out' produced for base, by gen_visit_fields() for each member,
    # and if variants were present
    if base or members or variants:
147
        ret += mcgen('''
148

149 150 151
out:
''')
    ret += mcgen('''
152 153 154
    error_propagate(errp, err);
}
''')
155 156 157
    return ret


158
def gen_visit_list(name, element_type):
159 160 161 162
    # FIXME: if *obj is NULL on entry, and the first visit_next_list()
    # assigns to *obj, while a later one fails, we should clean up *obj
    # rather than leaving it non-NULL. As currently written, the caller must
    # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
163 164
    return mcgen('''

165
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
166
{
P
Paolo Bonzini 已提交
167
    Error *err = NULL;
168
    GenericList *i, **prev;
169

170
    visit_start_list(v, name, &err);
171 172 173 174 175
    if (err) {
        goto out;
    }

    for (prev = (GenericList **)obj;
176
         !err && (i = visit_next_list(v, prev)) != NULL;
177
         prev = &i) {
178
        %(c_name)s *native_i = (%(c_name)s *)i;
179
        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
180
    }
181

182
    visit_end_list(v);
183 184
out:
    error_propagate(errp, err);
185 186
}
''',
187
                 c_name=c_name(name), c_elt_type=element_type.c_name())
188

189 190

def gen_visit_enum(name):
191 192
    return mcgen('''

193
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
194
{
E
Eric Blake 已提交
195
    int value = *obj;
196
    visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
E
Eric Blake 已提交
197
    *obj = value;
198 199
}
''',
200
                 c_name=c_name(name))
201

202

203
def gen_visit_alternate(name, variants):
204 205 206 207 208
    promote_int = 'true'
    for var in variants.variants:
        if var.type.alternate_qtype() == 'QTYPE_QINT':
            promote_int = 'false'

K
Kevin Wolf 已提交
209 210
    ret = mcgen('''

211
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
K
Kevin Wolf 已提交
212 213 214
{
    Error *err = NULL;

215
    visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
216 217 218
    if (err) {
        goto out;
    }
219
    visit_get_next_type(v, name, &(*obj)->type, %(promote_int)s, &err);
220
    if (err) {
221
        goto out_obj;
222
    }
223
    switch ((*obj)->type) {
K
Kevin Wolf 已提交
224
''',
225
                c_name=c_name(name), promote_int=promote_int)
K
Kevin Wolf 已提交
226

227
    for var in variants.variants:
K
Kevin Wolf 已提交
228
        ret += mcgen('''
229
    case %(case)s:
230
        visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
231
        break;
K
Kevin Wolf 已提交
232
''',
233
                     case=var.type.alternate_qtype(),
234 235
                     c_type=var.type.c_name(),
                     c_name=c_name(var.name))
K
Kevin Wolf 已提交
236 237

    ret += mcgen('''
238
    default:
239 240
        error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                   "%(name)s");
K
Kevin Wolf 已提交
241
    }
242
out_obj:
243
    visit_end_implicit_struct(v);
244 245
out:
    error_propagate(errp, err);
K
Kevin Wolf 已提交
246
}
247 248
''',
                 name=name)
K
Kevin Wolf 已提交
249 250 251

    return ret

252

253
def gen_visit_object(name, base, members, variants):
254
    ret = gen_visit_struct_fields(name, base, members, variants)
255

256 257 258 259
    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
    # rather than leaving it non-NULL. As currently written, the caller must
    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
260 261
    ret += mcgen('''

262
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
263
{
264 265
    Error *err = NULL;

266
    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
267 268 269
    if (err) {
        goto out;
    }
270 271 272
    if (!*obj) {
        goto out_obj;
    }
273
    visit_type_%(c_name)s_fields(v, obj, &err);
274 275
    error_propagate(errp, err);
    err = NULL;
276
out_obj:
277
    visit_end_struct(v, &err);
278 279
out:
    error_propagate(errp, err);
280
}
281 282
''',
                 c_name=c_name(name))
283

284 285
    return ret

286

287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
    def __init__(self):
        self.decl = None
        self.defn = None
        self._btin = None

    def visit_begin(self, schema):
        self.decl = ''
        self.defn = ''
        self._btin = guardstart('QAPI_VISIT_BUILTIN')

    def visit_end(self):
        # To avoid header dependency hell, we always generate
        # declarations for built-in types in our header files and
        # simply guard them.  See also do_builtins (command line
        # option -b).
        self._btin += guardend('QAPI_VISIT_BUILTIN')
        self.decl = self._btin + self.decl
        self._btin = None

307 308
    def visit_needed(self, entity):
        # Visit everything except implicit objects
309 310
        return not (entity.is_implicit() and
                    isinstance(entity, QAPISchemaObjectType))
311

312
    def visit_enum_type(self, name, info, values, prefix):
313 314 315 316 317 318 319 320 321
        # Special case for our lone builtin enum type
        # TODO use something cleaner than existence of info
        if not info:
            self._btin += gen_visit_decl(name, scalar=True)
            if do_builtins:
                self.defn += gen_visit_enum(name)
        else:
            self.decl += gen_visit_decl(name, scalar=True)
            self.defn += gen_visit_enum(name)
322 323 324 325 326 327 328 329 330 331 332 333 334

    def visit_array_type(self, name, info, element_type):
        decl = gen_visit_decl(name)
        defn = gen_visit_list(name, element_type)
        if isinstance(element_type, QAPISchemaBuiltinType):
            self._btin += decl
            if do_builtins:
                self.defn += defn
        else:
            self.decl += decl
            self.defn += defn

    def visit_object_type(self, name, info, base, members, variants):
335
        self.decl += gen_visit_decl(name)
336
        self.defn += gen_visit_object(name, base, members, variants)
337 338 339 340 341 342 343 344 345

    def visit_alternate_type(self, name, info, variants):
        self.decl += gen_visit_decl(name)
        self.defn += gen_visit_alternate(name, variants)

# If you link code generated from multiple schemata, you want only one
# instance of the code for built-in types.  Generate it only when
# do_builtins, enabled by command line option -b.  See also
# QAPISchemaGenVisitVisitor.visit_end().
346
do_builtins = False
347

348 349 350
(input_file, output_dir, do_c, do_h, prefix, opts) = \
    parse_command_line("b", ["builtins"])

351
for o, a in opts:
352
    if o in ("-b", "--builtins"):
353
        do_builtins = True
354

355
c_comment = '''
356 357 358 359 360 361 362 363 364 365 366 367
/*
 * schema-defined QAPI visitor functions
 *
 * Copyright IBM, Corp. 2011
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 * See the COPYING.LIB file in the top-level directory.
 *
 */
368 369
'''
h_comment = '''
370
/*
371
 * schema-defined QAPI visitor functions
372 373 374 375 376 377 378 379 380 381
 *
 * Copyright IBM, Corp. 2011
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 * See the COPYING.LIB file in the top-level directory.
 *
 */
382 383 384 385 386
'''

(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
                            'qapi-visit.c', 'qapi-visit.h',
                            c_comment, h_comment)
387

388
fdef.write(mcgen('''
389
#include "qemu/osdep.h"
390 391 392
#include "qemu-common.h"
#include "%(prefix)sqapi-visit.h"
''',
393
                 prefix=prefix))
394

395
fdecl.write(mcgen('''
396
#include "qapi/visitor.h"
397
#include "qapi/qmp/qerror.h"
398
#include "%(prefix)sqapi-types.h"
399

400
''',
401
                  prefix=prefix))
402

403 404 405 406 407
schema = QAPISchema(input_file)
gen = QAPISchemaGenVisitVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
408

409
close_output(fdef, fdecl)