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

from qapi import *

16

17 18 19 20 21
# variants must be emitted before their container; track what has already
# been output
objects_seen = set()


22
def gen_fwd_object_or_array(name):
23
    return mcgen('''
24

25
typedef struct %(c_name)s %(c_name)s;
26
''',
27 28
                 c_name=c_name(name))

29

30
def gen_array(name, element_type):
31
    return mcgen('''
M
Markus Armbruster 已提交
32

33 34
struct %(c_name)s {
    %(c_name)s *next;
35
    %(c_type)s value;
36
};
37
''',
38 39
                 c_name=c_name(name), c_type=element_type.c_type())

40

41
def gen_struct_fields(members):
42
    ret = ''
43 44 45
    for memb in members:
        if memb.optional:
            ret += mcgen('''
46 47
    bool has_%(c_name)s;
''',
48 49
                         c_name=c_name(memb.name))
        ret += mcgen('''
50 51
    %(c_type)s %(c_name)s;
''',
52
                     c_type=memb.type.c_type(), c_name=c_name(memb.name))
53 54
    return ret

55

56
def gen_object(name, base, members, variants):
57 58 59 60 61 62 63 64 65 66 67 68 69
    if name in objects_seen:
        return ''
    objects_seen.add(name)

    ret = ''
    if variants:
        for v in variants.variants:
            if (isinstance(v.type, QAPISchemaObjectType) and
                    not v.type.is_implicit()):
                ret += gen_object(v.type.name, v.type.base,
                                  v.type.local_members, v.type.variants)

    ret += mcgen('''
70 71 72

struct %(c_name)s {
''',
73
                 c_name=c_name(name))
74

75 76 77 78 79
    if base:
        ret += mcgen('''
    /* Members inherited from %(c_name)s: */
''',
                     c_name=base.c_name())
80
        ret += gen_struct_fields(base.members)
81 82 83
        ret += mcgen('''
    /* Own members: */
''')
84
    ret += gen_struct_fields(members)
85

86 87 88
    if variants:
        ret += gen_variants(variants)

89
    # Make sure that all structs have at least one field; this avoids
90 91 92
    # potential issues with attempting to malloc space for zero-length
    # structs in C, and also incompatibility with C++ (where an empty
    # struct is size 1).
93
    if not (base and base.members) and not members and not variants:
94
        ret += mcgen('''
95 96 97
    char qapi_dummy_field_for_empty_struct;
''')

98
    ret += mcgen('''
99 100
};
''')
101 102 103

    return ret

104

105 106 107 108 109 110 111 112 113 114 115 116 117
def gen_upcast(name, base):
    # C makes const-correctness ugly.  We have to cast away const to let
    # this function work for both const and non-const obj.
    return mcgen('''

static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
{
    return (%(base)s *)obj;
}
''',
                 c_name=c_name(name), base=base.c_name())


118
def gen_variants(variants):
119 120 121 122 123 124 125 126
    # FIXME: What purpose does data serve, besides preventing a union that
    # has a branch named 'data'? We use it in qapi-visit.py to decide
    # whether to bypass the switch statement if visiting the discriminator
    # failed; but since we 0-initialize structs, and cannot tell what
    # branch of the union is in use if the discriminator is invalid, there
    # should not be any data leaks even without a data pointer.  Or, if
    # 'data' is merely added to guarantee we don't have an empty union,
    # shouldn't we enforce that at .json parse time?
127
    ret = mcgen('''
128
    union { /* union tag is @%(c_name)s */
129
        void *data;
130
''',
131
                c_name=c_name(variants.tag_member.name))
132 133 134

    for var in variants.variants:
        # Ugly special case for simple union TODO get rid of it
135 136
        simple_union_type = var.simple_union_type()
        typ = simple_union_type or var.type
137
        ret += mcgen('''
138 139
        %(c_type)s %(c_name)s;
''',
140
                     c_type=typ.c_type(is_unboxed=not simple_union_type),
141
                     c_name=c_name(var.name))
142 143

    ret += mcgen('''
144
    } u;
145 146 147 148
''')

    return ret

149 150

def gen_type_cleanup_decl(name):
151
    ret = mcgen('''
152

153
void qapi_free_%(c_name)s(%(c_name)s *obj);
154
''',
155
                c_name=c_name(name))
156 157
    return ret

158 159

def gen_type_cleanup(name):
160
    ret = mcgen('''
161

162
void qapi_free_%(c_name)s(%(c_name)s *obj)
163
{
164
    QapiDeallocVisitor *qdv;
165 166 167 168 169 170
    Visitor *v;

    if (!obj) {
        return;
    }

171 172
    qdv = qapi_dealloc_visitor_new();
    v = qapi_dealloc_get_visitor(qdv);
173
    visit_type_%(c_name)s(v, NULL, &obj, NULL);
174
    qapi_dealloc_visitor_cleanup(qdv);
175 176
}
''',
177
                c_name=c_name(name))
178 179
    return ret

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
    def __init__(self):
        self.decl = None
        self.defn = None
        self._fwdecl = None
        self._btin = None

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

    def visit_end(self):
        self.decl = self._fwdecl + self.decl
        self._fwdecl = None
        # 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_TYPES_BUILTIN')
        self.decl = self._btin + self.decl
        self._btin = None

205 206
    def visit_needed(self, entity):
        # Visit everything except implicit objects
207 208
        return not (entity.is_implicit() and
                    isinstance(entity, QAPISchemaObjectType))
209

210
    def _gen_type_cleanup(self, name):
211 212
        self.decl += gen_type_cleanup_decl(name)
        self.defn += gen_type_cleanup(name)
213 214

    def visit_enum_type(self, name, info, values, prefix):
215 216 217 218 219 220 221 222
        # Special case for our lone builtin enum type
        # TODO use something cleaner than existence of info
        if not info:
            self._btin += gen_enum(name, values, prefix)
            if do_builtins:
                self.defn += gen_enum_lookup(name, values, prefix)
        else:
            self._fwdecl += gen_enum(name, values, prefix)
E
Eric Blake 已提交
223
            self.defn += gen_enum_lookup(name, values, prefix)
224 225 226 227 228

    def visit_array_type(self, name, info, element_type):
        if isinstance(element_type, QAPISchemaBuiltinType):
            self._btin += gen_fwd_object_or_array(name)
            self._btin += gen_array(name, element_type)
229
            self._btin += gen_type_cleanup_decl(name)
230
            if do_builtins:
231
                self.defn += gen_type_cleanup(name)
232 233 234 235 236 237
        else:
            self._fwdecl += gen_fwd_object_or_array(name)
            self.decl += gen_array(name, element_type)
            self._gen_type_cleanup(name)

    def visit_object_type(self, name, info, base, members, variants):
238
        self._fwdecl += gen_fwd_object_or_array(name)
239
        self.decl += gen_object(name, base, members, variants)
E
Eric Blake 已提交
240 241
        if base:
            self.decl += gen_upcast(name, base)
242
        self._gen_type_cleanup(name)
243 244 245

    def visit_alternate_type(self, name, info, variants):
        self._fwdecl += gen_fwd_object_or_array(name)
246
        self.decl += gen_object(name, None, [variants.tag_member], variants)
247 248 249 250 251 252
        self._gen_type_cleanup(name)

# 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
# QAPISchemaGenTypeVisitor.visit_end().
253
do_builtins = False
254

255 256 257
(input_file, output_dir, do_c, do_h, prefix, opts) = \
    parse_command_line("b", ["builtins"])

258
for o, a in opts:
259
    if o in ("-b", "--builtins"):
260
        do_builtins = True
261

262
c_comment = '''
263 264 265 266 267 268 269 270 271 272 273 274 275
/*
 * deallocation functions for schema-defined QAPI types
 *
 * Copyright IBM, Corp. 2011
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *  Michael Roth      <mdroth@linux.vnet.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.
 *
 */
276 277
'''
h_comment = '''
278 279 280 281 282 283 284 285 286 287 288 289
/*
 * schema-defined QAPI types
 *
 * 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.
 *
 */
290
'''
291

292 293 294
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
                            'qapi-types.c', 'qapi-types.h',
                            c_comment, h_comment)
295

296
fdef.write(mcgen('''
297
#include "qemu/osdep.h"
298 299 300 301 302 303
#include "qapi/dealloc-visitor.h"
#include "%(prefix)sqapi-types.h"
#include "%(prefix)sqapi-visit.h"
''',
                 prefix=prefix))

304
# To avoid circular headers, use only typedefs.h here, not qobject.h
305
fdecl.write(mcgen('''
306
#include "qemu/typedefs.h"
307
'''))
308

309 310 311 312 313
schema = QAPISchema(input_file)
gen = QAPISchemaGenTypeVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
314

315
close_output(fdef, fdecl)