ca56a822dd
Add a FIXME to remind us to fully audit whether removing the 'void *data' branch of each qapi union type can be done safely. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1438297637-26789-1-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
417 lines
11 KiB
Python
417 lines
11 KiB
Python
#
|
|
# QAPI types generator
|
|
#
|
|
# Copyright IBM, Corp. 2011
|
|
#
|
|
# Authors:
|
|
# Anthony Liguori <aliguori@us.ibm.com>
|
|
#
|
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
from ordereddict import OrderedDict
|
|
from qapi import *
|
|
|
|
def generate_fwd_builtin(name):
|
|
return mcgen('''
|
|
|
|
typedef struct %(name)sList
|
|
{
|
|
union {
|
|
%(type)s value;
|
|
uint64_t padding;
|
|
};
|
|
struct %(name)sList *next;
|
|
} %(name)sList;
|
|
''',
|
|
type=c_type(name),
|
|
name=name)
|
|
|
|
def generate_fwd_struct(name):
|
|
return mcgen('''
|
|
|
|
typedef struct %(name)s %(name)s;
|
|
|
|
typedef struct %(name)sList
|
|
{
|
|
union {
|
|
%(name)s *value;
|
|
uint64_t padding;
|
|
};
|
|
struct %(name)sList *next;
|
|
} %(name)sList;
|
|
''',
|
|
name=c_name(name))
|
|
|
|
def generate_fwd_enum_struct(name):
|
|
return mcgen('''
|
|
typedef struct %(name)sList
|
|
{
|
|
union {
|
|
%(name)s value;
|
|
uint64_t padding;
|
|
};
|
|
struct %(name)sList *next;
|
|
} %(name)sList;
|
|
''',
|
|
name=c_name(name))
|
|
|
|
def generate_struct_fields(members):
|
|
ret = ''
|
|
|
|
for argname, argentry, optional in parse_args(members):
|
|
if optional:
|
|
ret += mcgen('''
|
|
bool has_%(c_name)s;
|
|
''',
|
|
c_name=c_name(argname))
|
|
ret += mcgen('''
|
|
%(c_type)s %(c_name)s;
|
|
''',
|
|
c_type=c_type(argentry), c_name=c_name(argname))
|
|
|
|
return ret
|
|
|
|
def generate_struct(expr):
|
|
|
|
structname = expr.get('struct', "")
|
|
members = expr['data']
|
|
base = expr.get('base')
|
|
|
|
ret = mcgen('''
|
|
struct %(name)s
|
|
{
|
|
''',
|
|
name=c_name(structname))
|
|
|
|
if base:
|
|
ret += generate_struct_fields({'base': base})
|
|
|
|
ret += generate_struct_fields(members)
|
|
|
|
# Make sure that all structs have at least one field; this avoids
|
|
# 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).
|
|
if not base and not members:
|
|
ret += mcgen('''
|
|
char qapi_dummy_field_for_empty_struct;
|
|
''')
|
|
|
|
ret += mcgen('''
|
|
};
|
|
''')
|
|
|
|
return ret
|
|
|
|
def generate_enum_lookup(name, values):
|
|
ret = mcgen('''
|
|
const char * const %(name)s_lookup[] = {
|
|
''',
|
|
name=c_name(name))
|
|
for value in values:
|
|
index = c_enum_const(name, value)
|
|
ret += mcgen('''
|
|
[%(index)s] = "%(value)s",
|
|
''',
|
|
index = index, value = value)
|
|
|
|
max_index = c_enum_const(name, 'MAX')
|
|
ret += mcgen('''
|
|
[%(max_index)s] = NULL,
|
|
};
|
|
|
|
''',
|
|
max_index=max_index)
|
|
return ret
|
|
|
|
def generate_enum(name, values):
|
|
name = c_name(name)
|
|
lookup_decl = mcgen('''
|
|
extern const char * const %(name)s_lookup[];
|
|
''',
|
|
name=name)
|
|
|
|
enum_decl = mcgen('''
|
|
typedef enum %(name)s
|
|
{
|
|
''',
|
|
name=name)
|
|
|
|
# append automatically generated _MAX value
|
|
enum_values = values + [ 'MAX' ]
|
|
|
|
i = 0
|
|
for value in enum_values:
|
|
enum_full_value = c_enum_const(name, value)
|
|
enum_decl += mcgen('''
|
|
%(enum_full_value)s = %(i)d,
|
|
''',
|
|
enum_full_value = enum_full_value,
|
|
i=i)
|
|
i += 1
|
|
|
|
enum_decl += mcgen('''
|
|
} %(name)s;
|
|
''',
|
|
name=name)
|
|
|
|
return lookup_decl + enum_decl
|
|
|
|
def generate_alternate_qtypes(expr):
|
|
|
|
name = expr['alternate']
|
|
members = expr['data']
|
|
|
|
ret = mcgen('''
|
|
const int %(name)s_qtypes[QTYPE_MAX] = {
|
|
''',
|
|
name=c_name(name))
|
|
|
|
for key in members:
|
|
qtype = find_alternate_member_qtype(members[key])
|
|
assert qtype, "Invalid alternate member"
|
|
|
|
ret += mcgen('''
|
|
[%(qtype)s] = %(enum_const)s,
|
|
''',
|
|
qtype = qtype,
|
|
enum_const = c_enum_const(name + 'Kind', key))
|
|
|
|
ret += mcgen('''
|
|
};
|
|
''')
|
|
return ret
|
|
|
|
|
|
def generate_union(expr, meta):
|
|
|
|
name = c_name(expr[meta])
|
|
typeinfo = expr['data']
|
|
|
|
base = expr.get('base')
|
|
discriminator = expr.get('discriminator')
|
|
|
|
enum_define = discriminator_find_enum_define(expr)
|
|
if enum_define:
|
|
discriminator_type_name = enum_define['enum_name']
|
|
else:
|
|
discriminator_type_name = '%sKind' % (name)
|
|
|
|
ret = mcgen('''
|
|
struct %(name)s
|
|
{
|
|
''',
|
|
name=name)
|
|
if base:
|
|
ret += mcgen('''
|
|
/* Members inherited from %(c_name)s: */
|
|
''',
|
|
c_name=c_name(base))
|
|
base_fields = find_struct(base)['data']
|
|
ret += generate_struct_fields(base_fields)
|
|
ret += mcgen('''
|
|
/* Own members: */
|
|
''')
|
|
else:
|
|
assert not discriminator
|
|
ret += mcgen('''
|
|
%(discriminator_type_name)s kind;
|
|
''',
|
|
discriminator_type_name=c_name(discriminator_type_name))
|
|
|
|
# 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?
|
|
ret += mcgen('''
|
|
union { /* union tag is @%(c_name)s */
|
|
void *data;
|
|
''',
|
|
c_name=c_name(discriminator or 'kind'))
|
|
|
|
for key in typeinfo:
|
|
ret += mcgen('''
|
|
%(c_type)s %(c_name)s;
|
|
''',
|
|
c_type=c_type(typeinfo[key]),
|
|
c_name=c_name(key))
|
|
|
|
ret += mcgen('''
|
|
};
|
|
};
|
|
''')
|
|
if meta == 'alternate':
|
|
ret += mcgen('''
|
|
extern const int %(name)s_qtypes[];
|
|
''',
|
|
name=name)
|
|
|
|
|
|
return ret
|
|
|
|
def generate_type_cleanup_decl(name):
|
|
ret = mcgen('''
|
|
void qapi_free_%(name)s(%(c_type)s obj);
|
|
''',
|
|
c_type=c_type(name), name=c_name(name))
|
|
return ret
|
|
|
|
def generate_type_cleanup(name):
|
|
ret = mcgen('''
|
|
|
|
void qapi_free_%(name)s(%(c_type)s obj)
|
|
{
|
|
QapiDeallocVisitor *md;
|
|
Visitor *v;
|
|
|
|
if (!obj) {
|
|
return;
|
|
}
|
|
|
|
md = qapi_dealloc_visitor_new();
|
|
v = qapi_dealloc_get_visitor(md);
|
|
visit_type_%(name)s(v, &obj, NULL, NULL);
|
|
qapi_dealloc_visitor_cleanup(md);
|
|
}
|
|
''',
|
|
c_type=c_type(name), name=c_name(name))
|
|
return ret
|
|
|
|
do_builtins = False
|
|
|
|
(input_file, output_dir, do_c, do_h, prefix, opts) = \
|
|
parse_command_line("b", ["builtins"])
|
|
|
|
for o, a in opts:
|
|
if o in ("-b", "--builtins"):
|
|
do_builtins = True
|
|
|
|
c_comment = '''
|
|
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
'''
|
|
h_comment = '''
|
|
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
'''
|
|
|
|
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
|
|
'qapi-types.c', 'qapi-types.h',
|
|
c_comment, h_comment)
|
|
|
|
fdef.write(mcgen('''
|
|
#include "qapi/dealloc-visitor.h"
|
|
#include "%(prefix)sqapi-types.h"
|
|
#include "%(prefix)sqapi-visit.h"
|
|
|
|
''',
|
|
prefix=prefix))
|
|
|
|
fdecl.write(mcgen('''
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
'''))
|
|
|
|
exprs = parse_schema(input_file)
|
|
|
|
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
|
for typename in builtin_types.keys():
|
|
fdecl.write(generate_fwd_builtin(typename))
|
|
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
|
|
|
for expr in exprs:
|
|
ret = "\n"
|
|
if expr.has_key('struct'):
|
|
ret += generate_fwd_struct(expr['struct'])
|
|
elif expr.has_key('enum'):
|
|
ret += generate_enum(expr['enum'], expr['data']) + "\n"
|
|
ret += generate_fwd_enum_struct(expr['enum'])
|
|
fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
|
|
elif expr.has_key('union'):
|
|
ret += generate_fwd_struct(expr['union']) + "\n"
|
|
enum_define = discriminator_find_enum_define(expr)
|
|
if not enum_define:
|
|
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
|
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
|
|
expr['data'].keys()))
|
|
elif expr.has_key('alternate'):
|
|
ret += generate_fwd_struct(expr['alternate']) + "\n"
|
|
ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
|
|
fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
|
|
expr['data'].keys()))
|
|
fdef.write(generate_alternate_qtypes(expr))
|
|
else:
|
|
continue
|
|
fdecl.write(ret)
|
|
|
|
# to avoid header dependency hell, we always generate declarations
|
|
# for built-in types in our header files and simply guard them
|
|
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
|
for typename in builtin_types.keys():
|
|
fdecl.write(generate_type_cleanup_decl(typename + "List"))
|
|
fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
|
|
|
# ...this doesn't work for cases where we link in multiple objects that
|
|
# have the functions defined, so we use -b option to provide control
|
|
# over these cases
|
|
if do_builtins:
|
|
fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
|
|
for typename in builtin_types.keys():
|
|
fdef.write(generate_type_cleanup(typename + "List"))
|
|
fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
|
|
|
|
for expr in exprs:
|
|
ret = "\n"
|
|
if expr.has_key('struct'):
|
|
ret += generate_struct(expr) + "\n"
|
|
ret += generate_type_cleanup_decl(expr['struct'] + "List")
|
|
fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n")
|
|
ret += generate_type_cleanup_decl(expr['struct'])
|
|
fdef.write(generate_type_cleanup(expr['struct']) + "\n")
|
|
elif expr.has_key('union'):
|
|
ret += generate_union(expr, 'union')
|
|
ret += generate_type_cleanup_decl(expr['union'] + "List")
|
|
fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n")
|
|
ret += generate_type_cleanup_decl(expr['union'])
|
|
fdef.write(generate_type_cleanup(expr['union']) + "\n")
|
|
elif expr.has_key('alternate'):
|
|
ret += generate_union(expr, 'alternate')
|
|
ret += generate_type_cleanup_decl(expr['alternate'] + "List")
|
|
fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n")
|
|
ret += generate_type_cleanup_decl(expr['alternate'])
|
|
fdef.write(generate_type_cleanup(expr['alternate']) + "\n")
|
|
elif expr.has_key('enum'):
|
|
ret += generate_type_cleanup_decl(expr['enum'] + "List")
|
|
fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n")
|
|
else:
|
|
continue
|
|
fdecl.write(ret)
|
|
|
|
close_output(fdef, fdecl)
|