qapi-visit.py 11.7 KB
Newer Older
1 2 3 4
#
# QAPI visitor generator
#
# Copyright IBM, Corp. 2011
E
Eric Blake 已提交
5
# Copyright (C) 2014-2015 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
implicit_structs_seen = set()
19
struct_fields_seen = set()
20

21

22 23 24 25 26
def gen_visit_decl(name, scalar=False):
    c_type = c_name(name) + ' *'
    if not scalar:
        c_type += '*'
    return mcgen('''
27
void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, Error **errp);
28 29 30 31
''',
                 c_name=c_name(name), c_type=c_type)


32 33
def gen_visit_implicit_struct(typ):
    if typ in implicit_structs_seen:
34
        return ''
35 36
    implicit_structs_seen.add(typ)

37
    ret = ''
38
    if typ.name not in struct_fields_seen:
39 40 41
        # Need a forward declaration
        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

    ret += mcgen('''
47

48
static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
49 50 51
{
    Error *err = NULL;

52
    visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
53
    if (!err) {
54 55
        visit_type_%(c_type)s_fields(v, obj, errp);
        visit_end_implicit_struct(v, &err);
56 57 58 59
    }
    error_propagate(errp, err);
}
''',
60
                 c_type=typ.c_name())
61
    return ret
62

63 64

def gen_visit_struct_fields(name, base, members):
65 66
    struct_fields_seen.add(name)

67
    ret = ''
68

69
    if base:
70
        ret += gen_visit_implicit_struct(base)
71

72 73
    ret += mcgen('''

74
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
75 76
{
    Error *err = NULL;
M
Markus Armbruster 已提交
77

78
''',
79
                 c_name=c_name(name))
80
    push_indent()
P
Paolo Bonzini 已提交
81

82 83
    if base:
        ret += mcgen('''
84
visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
85 86 87
if (err) {
    goto out;
}
88
''',
89
                     c_type=base.c_name(), c_name=c_name('base'))
90

91 92
    for memb in members:
        if memb.optional:
93
            ret += mcgen('''
94
visit_optional(v, &(*obj)->has_%(c_name)s, "%(name)s", &err);
95
if (!err && (*obj)->has_%(c_name)s) {
96
''',
97
                         c_name=c_name(memb.name), name=memb.name)
98 99
            push_indent()

100
        ret += mcgen('''
101
visit_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 108 109
            pop_indent()
            ret += mcgen('''
}
110 111 112 113 114
''')
        ret += mcgen('''
if (err) {
    goto out;
}
P
Paolo Bonzini 已提交
115 116
''')

117
    pop_indent()
118
    if re.search('^ *goto out;', ret, re.MULTILINE):
119
        ret += mcgen('''
120

121 122 123
out:
''')
    ret += mcgen('''
124 125 126
    error_propagate(errp, err);
}
''')
127 128 129
    return ret


130 131 132
def gen_visit_struct(name, base, members):
    ret = gen_visit_struct_fields(name, base, members)

133 134 135 136
    # 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.
137 138
    ret += mcgen('''

139
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
140
{
141 142
    Error *err = NULL;

143
    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
144 145
    if (!err) {
        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 156

    return ret

157

158
def gen_visit_list(name, element_type):
159 160
    return mcgen('''

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

166
    visit_start_list(v, name, &err);
167 168 169 170 171
    if (err) {
        goto out;
    }

    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 179

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

187 188

def gen_visit_enum(name):
189 190
    return mcgen('''

191
void 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

199
def gen_visit_alternate(name, variants):
K
Kevin Wolf 已提交
200 201
    ret = mcgen('''

202
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
K
Kevin Wolf 已提交
203 204 205
{
    Error *err = NULL;

206
    visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
207 208 209
    if (err) {
        goto out;
    }
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) {
K
Kevin Wolf 已提交
215
''',
216
                c_name=c_name(name))
K
Kevin Wolf 已提交
217

218
    for var in variants.variants:
K
Kevin Wolf 已提交
219
        ret += mcgen('''
220
    case %(case)s:
221
        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
222
        break;
K
Kevin Wolf 已提交
223
''',
224 225 226 227
                     case=c_enum_const(variants.tag_member.type.name,
                                       var.name),
                     c_type=var.type.c_name(),
                     c_name=c_name(var.name))
K
Kevin Wolf 已提交
228 229

    ret += mcgen('''
230 231
    default:
        abort();
K
Kevin Wolf 已提交
232
    }
233
out_obj:
234 235
    error_propagate(errp, err);
    err = NULL;
236
    visit_end_implicit_struct(v, &err);
237 238
out:
    error_propagate(errp, err);
K
Kevin Wolf 已提交
239 240 241 242 243
}
''')

    return ret

244

245 246
def gen_visit_union(name, base, variants):
    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 253 254
    for var in variants.variants:
        # Ugly special case for simple union TODO get rid of it
        if not var.simple_union_type():
255
            ret += gen_visit_implicit_struct(var.type)
256

257 258
    ret += mcgen('''

259
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
260
{
261 262
    Error *err = NULL;

263
    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
264 265 266
    if (err) {
        goto out;
    }
267 268 269
    if (!*obj) {
        goto out_obj;
    }
270
''',
271
                 c_name=c_name(name), name=name)
272

273
    if base:
274
        ret += mcgen('''
275 276 277 278
    visit_type_%(c_name)s_fields(v, obj, &err);
    if (err) {
        goto out_obj;
    }
279
''',
280
                     c_name=c_name(name))
281

282
    tag_key = variants.tag_member.name
283 284
    if not variants.tag_name:
        # we pointlessly use a different key for simple unions
285
        tag_key = 'type'
286
    ret += mcgen('''
287 288 289 290 291 292 293 294
    visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
    if (err) {
        goto out_obj;
    }
    if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
        goto out_obj;
    }
    switch ((*obj)->%(c_name)s) {
295
''',
296
                 c_type=variants.tag_member.type.c_name(),
297 298 299 300
                 # TODO ugly special case for simple union
                 # Use same tag name in C as on the wire to get rid of
                 # it, then: c_name=c_name(variants.tag_member.name)
                 c_name=c_name(variants.tag_name or 'kind'),
301
                 name=tag_key)
302

303 304 305
    for var in variants.variants:
        # TODO ugly special case for simple union
        simple_union_type = var.simple_union_type()
306
        ret += mcgen('''
307
    case %(case)s:
308 309 310
''',
                     case=c_enum_const(variants.tag_member.type.name,
                                       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 316
''',
                         c_type=simple_union_type.c_name(),
                         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 322
''',
                         c_type=var.type.c_name(),
                         c_name=c_name(var.name))
323
        ret += mcgen('''
324
        break;
325
''')
326 327

    ret += mcgen('''
328 329
    default:
        abort();
330
    }
331 332 333 334 335 336
out_obj:
    error_propagate(errp, err);
    err = NULL;
    visit_end_union(v, !!(*obj)->data, &err);
    error_propagate(errp, err);
    err = NULL;
337
    visit_end_struct(v, &err);
338 339
out:
    error_propagate(errp, err);
340 341 342
}
''')

343 344
    return ret

345

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
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

    def visit_enum_type(self, name, info, values, prefix):
        self.decl += gen_visit_decl(name, scalar=True)
368
        self.defn += gen_visit_enum(name)
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397

    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):
        if info:
            self.decl += gen_visit_decl(name)
            if variants:
                assert not members      # not implemented
                self.defn += gen_visit_union(name, base, variants)
            else:
                self.defn += gen_visit_struct(name, base, members)

    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().
398
do_builtins = False
399

400 401 402
(input_file, output_dir, do_c, do_h, prefix, opts) = \
    parse_command_line("b", ["builtins"])

403
for o, a in opts:
404
    if o in ("-b", "--builtins"):
405
        do_builtins = True
406

407
c_comment = '''
408 409 410 411 412 413 414 415 416 417 418 419
/*
 * 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.
 *
 */
420 421
'''
h_comment = '''
422
/*
423
 * schema-defined QAPI visitor functions
424 425 426 427 428 429 430 431 432 433
 *
 * 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.
 *
 */
434 435 436 437 438
'''

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

440 441 442 443
fdef.write(mcgen('''
#include "qemu-common.h"
#include "%(prefix)sqapi-visit.h"
''',
444
                 prefix=prefix))
445

446
fdecl.write(mcgen('''
447
#include "qapi/visitor.h"
448
#include "%(prefix)sqapi-types.h"
449

450
''',
451
                  prefix=prefix))
452

453 454 455 456 457
schema = QAPISchema(input_file)
gen = QAPISchemaGenVisitVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
458

459
close_output(fdef, fdecl)