qapi-visit.py 11.1 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))
P
Paolo Bonzini 已提交
80

81 82
    if base:
        ret += mcgen('''
83
    visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
84
''',
85
                     c_type=base.c_name(), c_name=c_name('base'))
E
Eric Blake 已提交
86
        ret += gen_err_check()
87

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

90
    if re.search('^ *goto out;', ret, re.MULTILINE):
91
        ret += mcgen('''
92

93 94 95
out:
''')
    ret += mcgen('''
96 97 98
    error_propagate(errp, err);
}
''')
99 100 101
    return ret


102 103 104
def gen_visit_struct(name, base, members):
    ret = gen_visit_struct_fields(name, base, members)

105 106 107 108
    # 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.
109 110
    ret += mcgen('''

111
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
112
{
113 114
    Error *err = NULL;

115
    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
116 117
    if (!err) {
        if (*obj) {
118
            visit_type_%(c_name)s_fields(v, obj, errp);
119
        }
120
        visit_end_struct(v, &err);
121
    }
122
    error_propagate(errp, err);
123
}
124
''',
125
                 name=name, c_name=c_name(name))
126 127 128

    return ret

129

130
def gen_visit_list(name, element_type):
131 132
    return mcgen('''

133
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
134
{
P
Paolo Bonzini 已提交
135
    Error *err = NULL;
136
    GenericList *i, **prev;
137

138
    visit_start_list(v, name, &err);
139 140 141 142 143
    if (err) {
        goto out;
    }

    for (prev = (GenericList **)obj;
144
         !err && (i = visit_next_list(v, prev, &err)) != NULL;
145
         prev = &i) {
146
        %(c_name)s *native_i = (%(c_name)s *)i;
147
        visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err);
148
    }
149 150 151

    error_propagate(errp, err);
    err = NULL;
152
    visit_end_list(v, &err);
153 154
out:
    error_propagate(errp, err);
155 156
}
''',
157
                 c_name=c_name(name), c_elt_type=element_type.c_name())
158

159 160

def gen_visit_enum(name):
161 162
    return mcgen('''

163
void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
164
{
165
    visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
166 167
}
''',
168
                 c_name=c_name(name), name=name)
169

170

171
def gen_visit_alternate(name, variants):
K
Kevin Wolf 已提交
172 173
    ret = mcgen('''

174
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
K
Kevin Wolf 已提交
175 176 177
{
    Error *err = NULL;

178
    visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
179 180 181
    if (err) {
        goto out;
    }
182
    visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
183
    if (err) {
184
        goto out_obj;
185 186
    }
    switch ((*obj)->kind) {
K
Kevin Wolf 已提交
187
''',
188
                c_name=c_name(name))
K
Kevin Wolf 已提交
189

190
    for var in variants.variants:
K
Kevin Wolf 已提交
191
        ret += mcgen('''
192
    case %(case)s:
193
        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
194
        break;
K
Kevin Wolf 已提交
195
''',
196 197 198 199
                     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 已提交
200 201

    ret += mcgen('''
202 203
    default:
        abort();
K
Kevin Wolf 已提交
204
    }
205
out_obj:
206 207
    error_propagate(errp, err);
    err = NULL;
208
    visit_end_implicit_struct(v, &err);
209 210
out:
    error_propagate(errp, err);
K
Kevin Wolf 已提交
211 212 213 214 215
}
''')

    return ret

216

217 218
def gen_visit_union(name, base, variants):
    ret = ''
219

220
    if base:
221
        members = [m for m in base.members if m != variants.tag_member]
222
        ret += gen_visit_struct_fields(name, None, members)
223

224 225 226
    for var in variants.variants:
        # Ugly special case for simple union TODO get rid of it
        if not var.simple_union_type():
227
            ret += gen_visit_implicit_struct(var.type)
228

229 230
    ret += mcgen('''

231
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
232
{
233 234
    Error *err = NULL;

235
    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
236 237 238
    if (err) {
        goto out;
    }
239 240 241
    if (!*obj) {
        goto out_obj;
    }
242
''',
243
                 c_name=c_name(name), name=name)
244

245
    if base:
246
        ret += mcgen('''
247
    visit_type_%(c_name)s_fields(v, obj, &err);
248
''',
249
                     c_name=c_name(name))
E
Eric Blake 已提交
250
        ret += gen_err_check(label='out_obj')
251

252
    tag_key = variants.tag_member.name
253 254
    if not variants.tag_name:
        # we pointlessly use a different key for simple unions
255
        tag_key = 'type'
256
    ret += mcgen('''
257 258 259 260 261 262 263 264
    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) {
265
''',
266
                 c_type=variants.tag_member.type.c_name(),
267 268 269 270
                 # 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'),
271
                 name=tag_key)
272

273 274 275
    for var in variants.variants:
        # TODO ugly special case for simple union
        simple_union_type = var.simple_union_type()
276
        ret += mcgen('''
277
    case %(case)s:
278 279 280
''',
                     case=c_enum_const(variants.tag_member.type.name,
                                       var.name))
281
        if simple_union_type:
282
            ret += mcgen('''
283
        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err);
284 285 286
''',
                         c_type=simple_union_type.c_name(),
                         c_name=c_name(var.name))
287
        else:
288
            ret += mcgen('''
289
        visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
290 291 292
''',
                         c_type=var.type.c_name(),
                         c_name=c_name(var.name))
293
        ret += mcgen('''
294
        break;
295
''')
296 297

    ret += mcgen('''
298 299
    default:
        abort();
300
    }
301 302 303
out_obj:
    error_propagate(errp, err);
    err = NULL;
304 305 306
    if (*obj) {
        visit_end_union(v, !!(*obj)->data, &err);
    }
307 308
    error_propagate(errp, err);
    err = NULL;
309
    visit_end_struct(v, &err);
310 311
out:
    error_propagate(errp, err);
312 313 314
}
''')

315 316
    return ret

317

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
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)
340
        self.defn += gen_visit_enum(name)
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369

    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().
370
do_builtins = False
371

372 373 374
(input_file, output_dir, do_c, do_h, prefix, opts) = \
    parse_command_line("b", ["builtins"])

375
for o, a in opts:
376
    if o in ("-b", "--builtins"):
377
        do_builtins = True
378

379
c_comment = '''
380 381 382 383 384 385 386 387 388 389 390 391
/*
 * 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.
 *
 */
392 393
'''
h_comment = '''
394
/*
395
 * schema-defined QAPI visitor functions
396 397 398 399 400 401 402 403 404 405
 *
 * 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.
 *
 */
406 407 408 409 410
'''

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

412 413 414 415
fdef.write(mcgen('''
#include "qemu-common.h"
#include "%(prefix)sqapi-visit.h"
''',
416
                 prefix=prefix))
417

418
fdecl.write(mcgen('''
419
#include "qapi/visitor.h"
420
#include "%(prefix)sqapi-types.h"
421

422
''',
423
                  prefix=prefix))
424

425 426 427 428 429
schema = QAPISchema(input_file)
gen = QAPISchemaGenVisitVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
430

431
close_output(fdef, fdecl)