mirror of https://github.com/xqemu/xqemu.git
qapi script: support enum type as discriminator in union
By default, any union will automatically generate a enum type as "[UnionName]Kind" in C code, and it is duplicated when the discriminator is specified as a pre-defined enum type in schema. After this patch, the pre-defined enum type will be really used as the switch case condition in generated C code, if discriminator is an enum field. Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
This commit is contained in:
parent
b0b58195e4
commit
bceae7697f
|
@ -123,11 +123,15 @@ And it looks like this on the wire:
|
||||||
|
|
||||||
Flat union types avoid the nesting on the wire. They are used whenever a
|
Flat union types avoid the nesting on the wire. They are used whenever a
|
||||||
specific field of the base type is declared as the discriminator ('type' is
|
specific field of the base type is declared as the discriminator ('type' is
|
||||||
then no longer generated). The discriminator must always be a string field.
|
then no longer generated). The discriminator can be a string field or a
|
||||||
|
predefined enum field. If it is a string field, a hidden enum type will be
|
||||||
|
generated as "[UNION_NAME]Kind". If it is an enum field, a compile time check
|
||||||
|
will be done to verify the correctness. It is recommended to use an enum field.
|
||||||
The above example can then be modified as follows:
|
The above example can then be modified as follows:
|
||||||
|
|
||||||
|
{ 'enum': 'BlockdevDriver', 'data': [ 'raw', 'qcow2' ] }
|
||||||
{ 'type': 'BlockdevCommonOptions',
|
{ 'type': 'BlockdevCommonOptions',
|
||||||
'data': { 'driver': 'str', 'readonly': 'bool' } }
|
'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } }
|
||||||
{ 'union': 'BlockdevOptions',
|
{ 'union': 'BlockdevOptions',
|
||||||
'base': 'BlockdevCommonOptions',
|
'base': 'BlockdevCommonOptions',
|
||||||
'discriminator': 'driver',
|
'discriminator': 'driver',
|
||||||
|
|
|
@ -201,14 +201,21 @@ def generate_union(expr):
|
||||||
base = expr.get('base')
|
base = expr.get('base')
|
||||||
discriminator = expr.get('discriminator')
|
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('''
|
ret = mcgen('''
|
||||||
struct %(name)s
|
struct %(name)s
|
||||||
{
|
{
|
||||||
%(name)sKind kind;
|
%(discriminator_type_name)s kind;
|
||||||
union {
|
union {
|
||||||
void *data;
|
void *data;
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name,
|
||||||
|
discriminator_type_name=discriminator_type_name)
|
||||||
|
|
||||||
for key in typeinfo:
|
for key in typeinfo:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
@ -389,8 +396,11 @@ for expr in exprs:
|
||||||
fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
|
fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
|
||||||
elif expr.has_key('union'):
|
elif expr.has_key('union'):
|
||||||
ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
|
ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
|
||||||
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
enum_define = discriminator_find_enum_define(expr)
|
||||||
fdef.write(generate_enum_lookup('%sKind' % expr['union'], expr['data'].keys()))
|
if not enum_define:
|
||||||
|
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
||||||
|
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
|
||||||
|
expr['data'].keys()))
|
||||||
if expr.get('discriminator') == {}:
|
if expr.get('discriminator') == {}:
|
||||||
fdef.write(generate_anon_union_qtypes(expr))
|
fdef.write(generate_anon_union_qtypes(expr))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -259,10 +259,16 @@ def generate_visit_union(expr):
|
||||||
assert not base
|
assert not base
|
||||||
return generate_visit_anon_union(name, members)
|
return generate_visit_anon_union(name, members)
|
||||||
|
|
||||||
# There will always be a discriminator in the C switch code, by default it
|
enum_define = discriminator_find_enum_define(expr)
|
||||||
# is an enum type generated silently as "'%sKind' % (name)"
|
if enum_define:
|
||||||
ret = generate_visit_enum('%sKind' % name, members.keys())
|
# Use the enum type as discriminator
|
||||||
disc_type = '%sKind' % (name)
|
ret = ""
|
||||||
|
disc_type = enum_define['enum_name']
|
||||||
|
else:
|
||||||
|
# There will always be a discriminator in the C switch code, by default it
|
||||||
|
# is an enum type generated silently as "'%sKind' % (name)"
|
||||||
|
ret = generate_visit_enum('%sKind' % name, members.keys())
|
||||||
|
disc_type = '%sKind' % (name)
|
||||||
|
|
||||||
if base:
|
if base:
|
||||||
base_fields = find_struct(base)['data']
|
base_fields = find_struct(base)['data']
|
||||||
|
@ -298,15 +304,16 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
||||||
pop_indent()
|
pop_indent()
|
||||||
|
|
||||||
if not discriminator:
|
if not discriminator:
|
||||||
desc_type = "type"
|
disc_key = "type"
|
||||||
else:
|
else:
|
||||||
desc_type = discriminator
|
disc_key = discriminator
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
visit_type_%(name)sKind(m, &(*obj)->kind, "%(type)s", &err);
|
visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
switch ((*obj)->kind) {
|
switch ((*obj)->kind) {
|
||||||
''',
|
''',
|
||||||
name=name, type=desc_type)
|
disc_type = disc_type,
|
||||||
|
disc_key = disc_key)
|
||||||
|
|
||||||
for key in members:
|
for key in members:
|
||||||
if not discriminator:
|
if not discriminator:
|
||||||
|
@ -517,7 +524,11 @@ for expr in exprs:
|
||||||
ret += generate_visit_list(expr['union'], expr['data'])
|
ret += generate_visit_list(expr['union'], expr['data'])
|
||||||
fdef.write(ret)
|
fdef.write(ret)
|
||||||
|
|
||||||
ret = generate_decl_enum('%sKind' % expr['union'], expr['data'].keys())
|
enum_define = discriminator_find_enum_define(expr)
|
||||||
|
ret = ""
|
||||||
|
if not enum_define:
|
||||||
|
ret = generate_decl_enum('%sKind' % expr['union'],
|
||||||
|
expr['data'].keys())
|
||||||
ret += generate_declaration(expr['union'], expr['data'])
|
ret += generate_declaration(expr['union'], expr['data'])
|
||||||
fdecl.write(ret)
|
fdecl.write(ret)
|
||||||
elif expr.has_key('enum'):
|
elif expr.has_key('enum'):
|
||||||
|
|
|
@ -180,6 +180,25 @@ def find_base_fields(base):
|
||||||
return None
|
return None
|
||||||
return base_struct_define['data']
|
return base_struct_define['data']
|
||||||
|
|
||||||
|
# Return the discriminator enum define if discriminator is specified as an
|
||||||
|
# enum type, otherwise return None.
|
||||||
|
def discriminator_find_enum_define(expr):
|
||||||
|
base = expr.get('base')
|
||||||
|
discriminator = expr.get('discriminator')
|
||||||
|
|
||||||
|
if not (discriminator and base):
|
||||||
|
return None
|
||||||
|
|
||||||
|
base_fields = find_base_fields(base)
|
||||||
|
if not base_fields:
|
||||||
|
return None
|
||||||
|
|
||||||
|
discriminator_type = base_fields.get(discriminator)
|
||||||
|
if not discriminator_type:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return find_enum(discriminator_type)
|
||||||
|
|
||||||
def check_union(expr, expr_info):
|
def check_union(expr, expr_info):
|
||||||
name = expr['union']
|
name = expr['union']
|
||||||
base = expr.get('base')
|
base = expr.get('base')
|
||||||
|
@ -254,11 +273,17 @@ def parse_schema(fp):
|
||||||
add_enum(expr['enum'], expr['data'])
|
add_enum(expr['enum'], expr['data'])
|
||||||
elif expr.has_key('union'):
|
elif expr.has_key('union'):
|
||||||
add_union(expr)
|
add_union(expr)
|
||||||
add_enum('%sKind' % expr['union'])
|
|
||||||
elif expr.has_key('type'):
|
elif expr.has_key('type'):
|
||||||
add_struct(expr)
|
add_struct(expr)
|
||||||
exprs.append(expr)
|
exprs.append(expr)
|
||||||
|
|
||||||
|
# Try again for hidden UnionKind enum
|
||||||
|
for expr_elem in schema.exprs:
|
||||||
|
expr = expr_elem['expr']
|
||||||
|
if expr.has_key('union'):
|
||||||
|
if not discriminator_find_enum_define(expr):
|
||||||
|
add_enum('%sKind' % expr['union'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_exprs(schema)
|
check_exprs(schema)
|
||||||
except QAPIExprError, e:
|
except QAPIExprError, e:
|
||||||
|
|
|
@ -145,7 +145,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
|
||||||
unclosed-list.json unclosed-object.json unclosed-string.json \
|
unclosed-list.json unclosed-object.json unclosed-string.json \
|
||||||
duplicate-key.json union-invalid-base.json flat-union-no-base.json \
|
duplicate-key.json union-invalid-base.json flat-union-no-base.json \
|
||||||
flat-union-invalid-discriminator.json \
|
flat-union-invalid-discriminator.json \
|
||||||
flat-union-invalid-branch-key.json)
|
flat-union-invalid-branch-key.json flat-union-reverse-define.json)
|
||||||
|
|
||||||
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
|
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -0,0 +1,17 @@
|
||||||
|
{ 'union': 'TestUnion',
|
||||||
|
'base': 'TestBase',
|
||||||
|
'discriminator': 'enum1',
|
||||||
|
'data': { 'value1': 'TestTypeA',
|
||||||
|
'value2': 'TestTypeB' } }
|
||||||
|
|
||||||
|
{ 'type': 'TestBase',
|
||||||
|
'data': { 'enum1': 'TestEnum' } }
|
||||||
|
|
||||||
|
{ 'enum': 'TestEnum',
|
||||||
|
'data': [ 'value1', 'value2' ] }
|
||||||
|
|
||||||
|
{ 'type': 'TestTypeA',
|
||||||
|
'data': { 'string': 'str' } }
|
||||||
|
|
||||||
|
{ 'type': 'TestTypeB',
|
||||||
|
'data': { 'integer': 'int' } }
|
|
@ -0,0 +1,9 @@
|
||||||
|
[OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))]),
|
||||||
|
OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
|
||||||
|
OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
|
||||||
|
OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
|
||||||
|
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
|
||||||
|
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
|
||||||
|
[OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
|
||||||
|
OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
|
||||||
|
OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
|
Loading…
Reference in New Issue