qapi: Add feature flags to remaining definitions

In v4.1.0, we added feature flags just to struct types (commit
6a8c0b5102^..f3ed93d545), to satisfy an immediate need (commit
c9d4070991 "file-posix: Add dynamic-auto-read-only QAPI feature").  In
v4.2.0, we added them to commands (commit 23394b4c39 "qapi: Add
feature flags to commands") to satisfy another immediate need (commit
d76744e65e "qapi: Allow introspecting fix for savevm's cooperation
with blockdev").

Add them to the remaining definitions: enumeration types, union types,
alternate types, and events.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200317115459.31821-13-armbru@redhat.com>
This commit is contained in:
Markus Armbruster 2020-03-17 12:54:37 +01:00
parent e4405b3069
commit 013b4efc9b
17 changed files with 242 additions and 121 deletions

View File

@ -172,7 +172,8 @@ Syntax:
ENUM = { 'enum': STRING,
'data': [ ENUM-VALUE, ... ],
'*prefix': STRING,
'*if': COND }
'*if': COND,
'*features': FEATURES }
ENUM-VALUE = STRING
| { 'name': STRING, '*if': COND }
@ -207,6 +208,9 @@ the job satisfactorily.
The optional 'if' member specifies a conditional. See "Configuring
the schema" below for more on this.
The optional 'features' member specifies features. See "Features"
below for more on this.
=== Type references and array types ===
@ -279,12 +283,14 @@ below for more on this.
Syntax:
UNION = { 'union': STRING,
'data': BRANCHES,
'*if': COND }
'*if': COND,
'*features': FEATURES }
| { 'union': STRING,
'data': BRANCHES,
'base': ( MEMBERS | STRING ),
'discriminator': STRING,
'*if': COND }
'*if': COND,
'*features': FEATURES }
BRANCHES = { BRANCH, ... }
BRANCH = STRING : TYPE-REF
| STRING : { 'type': TYPE-REF, '*if': COND }
@ -391,13 +397,17 @@ is identical on the wire to:
The optional 'if' member specifies a conditional. See "Configuring
the schema" below for more on this.
The optional 'features' member specifies features. See "Features"
below for more on this.
=== Alternate types ===
Syntax:
ALTERNATE = { 'alternate': STRING,
'data': ALTERNATIVES,
'*if': COND }
'*if': COND,
'*features': FEATURES }
ALTERNATIVES = { ALTERNATIVE, ... }
ALTERNATIVE = STRING : STRING
| STRING : { 'type': STRING, '*if': COND }
@ -441,6 +451,9 @@ following example objects:
The optional 'if' member specifies a conditional. See "Configuring
the schema" below for more on this.
The optional 'features' member specifies features. See "Features"
below for more on this.
=== Commands ===
@ -584,6 +597,9 @@ started with --preconfig.
The optional 'if' member specifies a conditional. See "Configuring
the schema" below for more on this.
The optional 'features' member specifies features. See "Features"
below for more on this.
=== Events ===
@ -595,7 +611,8 @@ Syntax:
'data': STRING,
'boxed': true,
)
'*if': COND }
'*if': COND,
'*features': FEATURES }
Member 'event' names the event. This is the event name used in the
Client JSON Protocol.
@ -628,6 +645,9 @@ complex type. See section "Code generated for events" for examples.
The optional 'if' member specifies a conditional. See "Configuring
the schema" below for more on this.
The optional 'features' member specifies features. See "Features"
below for more on this.
=== Features ===
@ -966,8 +986,9 @@ schema, along with the SchemaInfo type. This text attempts to give an
overview how things work. For details you need to consult the QAPI
schema.
SchemaInfo objects have common members "name", "meta-type", and
additional variant members depending on the value of meta-type.
SchemaInfo objects have common members "name", "meta-type",
"features", and additional variant members depending on the value of
meta-type.
Each SchemaInfo object describes a wire ABI entity of a certain
meta-type: a command, event or one of several kinds of type.
@ -980,19 +1001,21 @@ not. Therefore, the SchemaInfo for types have auto-generated
meaningless names. For readability, the examples in this section use
meaningful type names instead.
Optional member "features" exposes the entity's feature strings as a
JSON array of strings.
To examine a type, start with a command or event using it, then follow
references by name.
QAPI schema definitions not reachable that way are omitted.
The SchemaInfo for a command has meta-type "command", and variant
members "arg-type", "ret-type", "allow-oob", and "features". On the
wire, the "arguments" member of a client's "execute" command must
conform to the object type named by "arg-type". The "return" member
that the server passes in a success response conforms to the type
named by "ret-type". When "allow-oob" is true, it means the command
supports out-of-band execution. It defaults to false. "features"
exposes the command's feature strings as a JSON array of strings.
members "arg-type", "ret-type" and "allow-oob". On the wire, the
"arguments" member of a client's "execute" command must conform to the
object type named by "arg-type". The "return" member that the server
passes in a success response conforms to the type named by "ret-type".
When "allow-oob" is true, it means the command supports out-of-band
execution. It defaults to false.
If the command takes no arguments, "arg-type" names an object type
without members. Likewise, if the command returns nothing, "ret-type"
@ -1027,8 +1050,7 @@ Example: the SchemaInfo for EVENT_C from section Events
The SchemaInfo for struct and union types has meta-type "object".
The SchemaInfo for a struct type has variant members "members" and
"features".
The SchemaInfo for a struct type has variant member "members".
The SchemaInfo for a union type additionally has variant members "tag"
and "variants".

View File

@ -89,12 +89,18 @@
#
# @meta-type: the entity's meta type, inherited from @base.
#
# @features: names of features associated with the entity, in no
# particular order.
# (since 4.1 for object types, 4.2 for commands, 5.0 for
# the rest)
#
# Additional members depend on the value of @meta-type.
#
# Since: 2.5
##
{ 'union': 'SchemaInfo',
'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' },
'base': { 'name': 'str', 'meta-type': 'SchemaMetaType',
'*features': [ 'str' ] },
'discriminator': 'meta-type',
'data': {
'builtin': 'SchemaInfoBuiltin',
@ -174,9 +180,6 @@
# and may even differ from the order of the values of the
# enum type of the @tag.
#
# @features: names of features associated with the type, in no particular
# order. (since: 4.1)
#
# Values of this type are JSON object on the wire.
#
# Since: 2.5
@ -184,8 +187,7 @@
{ 'struct': 'SchemaInfoObject',
'data': { 'members': [ 'SchemaInfoObjectMember' ],
'*tag': 'str',
'*variants': [ 'SchemaInfoObjectVariant' ],
'*features': [ 'str' ] } }
'*variants': [ 'SchemaInfoObjectVariant' ] } }
##
# @SchemaInfoObjectMember:
@ -266,17 +268,13 @@
# @allow-oob: whether the command allows out-of-band execution,
# defaults to false (Since: 2.12)
#
# @features: names of features associated with the command, in no particular
# order. (since 4.2)
#
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
'data': { 'arg-type': 'str', 'ret-type': 'str',
'*allow-oob': 'bool',
'*features': [ 'str' ] } }
'*allow-oob': 'bool' } }
##
# @SchemaInfoEvent:

View File

@ -243,7 +243,7 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
def write(self, output_dir):
self._gen.write(output_dir)
def visit_enum_type(self, name, info, ifcond, members, prefix):
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
doc = self.cur_doc
self._gen.add(texi_type('Enum', doc, ifcond,
texi_members(doc, 'Values',
@ -257,7 +257,7 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
self._gen.add(texi_type('Object', doc, ifcond,
texi_members(doc, 'Members', base, variants)))
def visit_alternate_type(self, name, info, ifcond, variants):
def visit_alternate_type(self, name, info, ifcond, features, variants):
doc = self.cur_doc
self._gen.add(texi_type('Alternate', doc, ifcond,
texi_members(doc, 'Members')))
@ -270,7 +270,7 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
texi_arguments(doc,
arg_type if boxed else None)))
def visit_event(self, name, info, ifcond, arg_type, boxed):
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
doc = self.cur_doc
self._gen.add(texi_msg('Event', doc, ifcond,
texi_arguments(doc,

View File

@ -189,7 +189,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
event_emit=self._event_emit_name,
event_enum=self._event_enum_name))
def visit_event(self, name, info, ifcond, arg_type, boxed):
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
self._genc.add(gen_event_send(name, arg_type, boxed,

View File

@ -219,7 +219,6 @@ def check_struct(expr, info):
check_type(members, info, "'data'", allow_dict=name)
check_type(expr.get('base'), info, "'base'")
check_features(expr.get('features'), info)
def check_union(expr, info):
@ -267,7 +266,6 @@ def check_command(expr, info):
raise QAPISemError(info, "'boxed': true requires 'data'")
check_type(args, info, "'data'", allow_dict=not boxed)
check_type(rets, info, "'returns'", allow_array=True)
check_features(expr.get('features'), info)
def check_event(expr, info):
@ -319,18 +317,18 @@ def check_exprs(exprs):
if meta == 'enum':
check_keys(expr, info, meta,
['enum', 'data'], ['if', 'prefix'])
['enum', 'data'], ['if', 'features', 'prefix'])
check_enum(expr, info)
elif meta == 'union':
check_keys(expr, info, meta,
['union', 'data'],
['base', 'discriminator', 'if'])
['base', 'discriminator', 'if', 'features'])
normalize_members(expr.get('base'))
normalize_members(expr['data'])
check_union(expr, info)
elif meta == 'alternate':
check_keys(expr, info, meta,
['alternate', 'data'], ['if'])
['alternate', 'data'], ['if', 'features'])
normalize_members(expr['data'])
check_alternate(expr, info)
elif meta == 'struct':
@ -348,13 +346,14 @@ def check_exprs(exprs):
check_command(expr, info)
elif meta == 'event':
check_keys(expr, info, meta,
['event'], ['data', 'boxed', 'if'])
['event'], ['data', 'boxed', 'if', 'features'])
normalize_members(expr.get('data'))
check_event(expr, info)
else:
assert False, 'unexpected meta type'
check_if(expr, info, meta)
check_features(expr.get('features'), info)
check_flags(expr, info)
return exprs

View File

@ -144,7 +144,7 @@ const QLitObject %(c_name)s = %(c_string)s;
return '[' + self._use_type(typ.element_type) + ']'
return self._name(typ.name)
def _gen_qlit(self, name, mtype, obj, ifcond):
def _gen_qlit(self, name, mtype, obj, ifcond, features):
extra = {}
if mtype not in ('command', 'event', 'builtin', 'array'):
if not self._unmask:
@ -154,6 +154,8 @@ const QLitObject %(c_name)s = %(c_string)s;
name = self._name(name)
obj['name'] = name
obj['meta-type'] = mtype
if features:
obj['features'] = [(f.name, {'if': f.ifcond}) for f in features]
if ifcond:
extra['if'] = ifcond
if extra:
@ -178,18 +180,18 @@ const QLitObject %(c_name)s = %(c_string)s;
{'if': variant.ifcond})
def visit_builtin_type(self, name, info, json_type):
self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
self._gen_qlit(name, 'builtin', {'json-type': json_type}, [], None)
def visit_enum_type(self, name, info, ifcond, members, prefix):
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
self._gen_qlit(name, 'enum',
{'values':
[(m.name, {'if': m.ifcond}) for m in members]},
ifcond)
ifcond, features)
def visit_array_type(self, name, info, ifcond, element_type):
element = self._use_type(element_type)
self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
ifcond)
ifcond, None)
def visit_object_type_flat(self, name, info, ifcond, members, variants,
features):
@ -197,16 +199,15 @@ const QLitObject %(c_name)s = %(c_string)s;
if variants:
obj.update(self._gen_variants(variants.tag_member.name,
variants.variants))
if features:
obj['features'] = [(f.name, {'if': f.ifcond}) for f in features]
self._gen_qlit(name, 'object', obj, ifcond)
self._gen_qlit(name, 'object', obj, ifcond, features)
def visit_alternate_type(self, name, info, ifcond, variants):
def visit_alternate_type(self, name, info, ifcond, features, variants):
self._gen_qlit(name, 'alternate',
{'members': [
({'type': self._use_type(m.type)}, {'if': m.ifcond})
for m in variants.variants]}, ifcond)
for m in variants.variants]},
ifcond, features)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig,
@ -217,16 +218,12 @@ const QLitObject %(c_name)s = %(c_string)s;
'ret-type': self._use_type(ret_type)}
if allow_oob:
obj['allow-oob'] = allow_oob
self._gen_qlit(name, 'command', obj, ifcond, features)
if features:
obj['features'] = [(f.name, {'if': f.ifcond}) for f in features]
self._gen_qlit(name, 'command', obj, ifcond)
def visit_event(self, name, info, ifcond, arg_type, boxed):
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)},
ifcond)
ifcond, features)
def gen_introspect(schema, output_dir, prefix, opt_unmask):

View File

@ -109,7 +109,7 @@ class QAPISchemaVisitor:
def visit_builtin_type(self, name, info, json_type):
pass
def visit_enum_type(self, name, info, ifcond, members, prefix):
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
pass
def visit_array_type(self, name, info, ifcond, element_type):
@ -123,7 +123,7 @@ class QAPISchemaVisitor:
features):
pass
def visit_alternate_type(self, name, info, ifcond, variants):
def visit_alternate_type(self, name, info, ifcond, features, variants):
pass
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
@ -131,7 +131,7 @@ class QAPISchemaVisitor:
features):
pass
def visit_event(self, name, info, ifcond, arg_type, boxed):
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
pass
@ -234,8 +234,8 @@ class QAPISchemaBuiltinType(QAPISchemaType):
class QAPISchemaEnumType(QAPISchemaType):
meta = 'enum'
def __init__(self, name, info, doc, ifcond, members, prefix):
super().__init__(name, info, doc, ifcond)
def __init__(self, name, info, doc, ifcond, features, members, prefix):
super().__init__(name, info, doc, ifcond, features)
for m in members:
assert isinstance(m, QAPISchemaEnumMember)
m.set_defined_in(name)
@ -271,15 +271,16 @@ class QAPISchemaEnumType(QAPISchemaType):
def visit(self, visitor):
super().visit(visitor)
visitor.visit_enum_type(self.name, self.info, self.ifcond,
self.members, self.prefix)
visitor.visit_enum_type(
self.name, self.info, self.ifcond, self.features,
self.members, self.prefix)
class QAPISchemaArrayType(QAPISchemaType):
meta = 'array'
def __init__(self, name, info, element_type):
super().__init__(name, info, None, None)
super().__init__(name, info, None)
assert isinstance(element_type, str)
self._element_type_name = element_type
self.element_type = None
@ -325,8 +326,8 @@ class QAPISchemaArrayType(QAPISchemaType):
class QAPISchemaObjectType(QAPISchemaType):
def __init__(self, name, info, doc, ifcond,
base, local_members, variants, features):
def __init__(self, name, info, doc, ifcond, features,
base, local_members, variants):
# struct has local_members, optional base, and no variants
# flat union has base, variants, and no local_members
# simple union has local_members, variants, and no base
@ -622,8 +623,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
class QAPISchemaAlternateType(QAPISchemaType):
meta = 'alternate'
def __init__(self, name, info, doc, ifcond, variants):
super().__init__(name, info, doc, ifcond)
def __init__(self, name, info, doc, ifcond, features, variants):
super().__init__(name, info, doc, ifcond, features)
assert isinstance(variants, QAPISchemaObjectTypeVariants)
assert variants.tag_member
variants.set_defined_in(name)
@ -683,16 +684,16 @@ class QAPISchemaAlternateType(QAPISchemaType):
def visit(self, visitor):
super().visit(visitor)
visitor.visit_alternate_type(self.name, self.info, self.ifcond,
self.variants)
visitor.visit_alternate_type(
self.name, self.info, self.ifcond, self.features, self.variants)
class QAPISchemaCommand(QAPISchemaEntity):
meta = 'command'
def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
gen, success_response, boxed, allow_oob, allow_preconfig,
features):
def __init__(self, name, info, doc, ifcond, features,
arg_type, ret_type,
gen, success_response, boxed, allow_oob, allow_preconfig):
super().__init__(name, info, doc, ifcond, features)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
@ -755,8 +756,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
class QAPISchemaEvent(QAPISchemaEntity):
meta = 'event'
def __init__(self, name, info, doc, ifcond, arg_type, boxed):
super().__init__(name, info, doc, ifcond)
def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
super().__init__(name, info, doc, ifcond, features)
assert not arg_type or isinstance(arg_type, str)
self._arg_type_name = arg_type
self.arg_type = None
@ -787,8 +788,9 @@ class QAPISchemaEvent(QAPISchemaEntity):
def visit(self, visitor):
super().visit(visitor)
visitor.visit_event(self.name, self.info, self.ifcond,
self.arg_type, self.boxed)
visitor.visit_event(
self.name, self.info, self.ifcond, self.features,
self.arg_type, self.boxed)
class QAPISchema:
@ -893,7 +895,7 @@ class QAPISchema:
('null', 'null', 'QNull' + pointer_suffix)]:
self._def_builtin_type(*t)
self.the_empty_object_type = QAPISchemaObjectType(
'q_empty', None, None, None, None, [], None, [])
'q_empty', None, None, None, None, None, [], None)
self._def_entity(self.the_empty_object_type)
qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
@ -901,10 +903,11 @@ class QAPISchema:
qtype_values = self._make_enum_members(
[{'name': n} for n in qtypes], None)
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
qtype_values, 'QTYPE'))
def _make_features(self, features, info):
def _make_features(self, expr, info):
features = expr.get('features', [])
return [QAPISchemaFeature(f['name'], info, f.get('if'))
for f in features]
@ -916,7 +919,8 @@ class QAPISchema:
# See also QAPISchemaObjectTypeMember.describe()
name = name + 'Kind' # reserved by check_defn_name_str()
self._def_entity(QAPISchemaEnumType(
name, info, None, ifcond, self._make_enum_members(values, info),
name, info, None, ifcond, None,
self._make_enum_members(values, info),
None))
return name
@ -944,8 +948,8 @@ class QAPISchema:
# TODO kill simple unions or implement the disjunction
assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
else:
self._def_entity(QAPISchemaObjectType(name, info, None, ifcond,
None, members, None, []))
self._def_entity(QAPISchemaObjectType(
name, info, None, ifcond, None, None, members, None))
return name
def _def_enum_type(self, expr, info, doc):
@ -953,8 +957,9 @@ class QAPISchema:
data = expr['data']
prefix = expr.get('prefix')
ifcond = expr.get('if')
features = self._make_features(expr, info)
self._def_entity(QAPISchemaEnumType(
name, info, doc, ifcond,
name, info, doc, ifcond, features,
self._make_enum_members(data, info), prefix))
def _make_member(self, name, typ, ifcond, info):
@ -976,12 +981,11 @@ class QAPISchema:
base = expr.get('base')
data = expr['data']
ifcond = expr.get('if')
features = expr.get('features', [])
features = self._make_features(expr, info)
self._def_entity(QAPISchemaObjectType(
name, info, doc, ifcond, base,
name, info, doc, ifcond, features, base,
self._make_members(data, info),
None,
self._make_features(features, info)))
None))
def _make_variant(self, case, typ, ifcond, info):
return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
@ -1000,6 +1004,7 @@ class QAPISchema:
data = expr['data']
base = expr.get('base')
ifcond = expr.get('if')
features = self._make_features(expr, info)
tag_name = expr.get('discriminator')
tag_member = None
if isinstance(base, dict):
@ -1020,21 +1025,22 @@ class QAPISchema:
tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
members = [tag_member]
self._def_entity(
QAPISchemaObjectType(name, info, doc, ifcond, base, members,
QAPISchemaObjectType(name, info, doc, ifcond, features,
base, members,
QAPISchemaObjectTypeVariants(
tag_name, info, tag_member, variants),
[]))
tag_name, info, tag_member, variants)))
def _def_alternate_type(self, expr, info, doc):
name = expr['alternate']
data = expr['data']
ifcond = expr.get('if')
features = self._make_features(expr, info)
variants = [self._make_variant(key, value['type'], value.get('if'),
info)
for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
self._def_entity(
QAPISchemaAlternateType(name, info, doc, ifcond,
QAPISchemaAlternateType(name, info, doc, ifcond, features,
QAPISchemaObjectTypeVariants(
None, info, tag_member, variants)))
@ -1048,27 +1054,31 @@ class QAPISchema:
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
ifcond = expr.get('if')
features = expr.get('features', [])
features = self._make_features(expr, info)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, ifcond, 'arg', self._make_members(data, info))
name, info, ifcond,
'arg', self._make_members(data, info))
if isinstance(rets, list):
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
data, rets,
gen, success_response,
boxed, allow_oob, allow_preconfig,
self._make_features(features, info)))
boxed, allow_oob, allow_preconfig))
def _def_event(self, expr, info, doc):
name = expr['event']
data = expr.get('data')
boxed = expr.get('boxed', False)
ifcond = expr.get('if')
features = self._make_features(expr, info)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, ifcond, 'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
name, info, ifcond,
'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
data, boxed))
def _def_exprs(self, exprs):
for expr_elem in exprs:

View File

@ -278,7 +278,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
self._genh.add(gen_type_cleanup_decl(name))
self._genc.add(gen_type_cleanup(name))
def visit_enum_type(self, name, info, ifcond, members, prefix):
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.preamble_add(gen_enum(name, members, prefix))
self._genc.add(gen_enum_lookup(name, members, prefix))
@ -306,7 +306,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
# implicit types won't be directly allocated/freed
self._gen_type_cleanup(name)
def visit_alternate_type(self, name, info, ifcond, variants):
def visit_alternate_type(self, name, info, ifcond, features, variants):
with ifcontext(ifcond, self._genh):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_object(name, ifcond, None,

View File

@ -316,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
''',
types=types))
def visit_enum_type(self, name, info, ifcond, members, prefix):
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name, scalar=True))
self._genc.add(gen_visit_enum(name))
@ -342,7 +342,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_object(name, base, members, variants))
def visit_alternate_type(self, name, info, ifcond, variants):
def visit_alternate_type(self, name, info, ifcond, features, variants):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_alternate(name, variants))

View File

@ -1,3 +1,3 @@
alternate-base.json: In alternate 'Alt':
alternate-base.json:4: alternate has unknown key 'base'
Valid keys are 'alternate', 'data', 'if'.
Valid keys are 'alternate', 'data', 'features', 'if'.

View File

@ -53,10 +53,14 @@
# @Enum:
# @one: The _one_ {and only}
#
# Features:
# @enum-feat: Also _one_ {and only}
#
# @two is undocumented
##
{ 'enum': 'Enum', 'data':
[ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ],
'features': [ 'enum-feat' ],
'if': 'defined(IFCOND)' }
##
@ -86,24 +90,34 @@
##
# @Object:
# Features:
# @union-feat1: a feature
##
{ 'union': 'Object',
'features': [ 'union-feat1' ],
'base': 'Base',
'discriminator': 'base1',
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
##
# @SugaredUnion:
# Features:
# @union-feat2: a feature
##
{ 'union': 'SugaredUnion',
'features': [ 'union-feat2' ],
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
##
# @Alternate:
# @i: an integer
# @b is undocumented
#
# Features:
# @alt-feat: a feature
##
{ 'alternate': 'Alternate',
'features': [ 'alt-feat' ],
'data': { 'i': 'int', 'b': 'bool' } }
##
@ -160,6 +174,9 @@
##
# @EVT-BOXED:
# Features:
# @feat3: a feature
##
{ 'event': 'EVT-BOXED', 'boxed': true,
'features': [ 'feat3' ],
'data': 'Object' }

View File

@ -15,6 +15,7 @@ enum Enum
if ['defined(IFONE)']
member two
if ['defined(IFCOND)']
feature enum-feat
object Base
member base1: Enum optional=False
object Variant1
@ -28,6 +29,7 @@ object Object
case one: Variant1
case two: Variant2
if ['IFTWO']
feature union-feat1
object q_obj_Variant1-wrapper
member data: Variant1 optional=False
object q_obj_Variant2-wrapper
@ -42,10 +44,12 @@ object SugaredUnion
case one: q_obj_Variant1-wrapper
case two: q_obj_Variant2-wrapper
if ['IFTWO']
feature union-feat2
alternate Alternate
tag type
case i: int
case b: bool
feature alt-feat
object q_obj_cmd-arg
member arg1: int optional=False
member arg2: str optional=True
@ -60,6 +64,7 @@ command cmd-boxed Object -> None
feature cmd-feat2
event EVT-BOXED Object
boxed=True
feature feat3
doc freeform
body=
= Section
@ -112,6 +117,8 @@ doc symbol=Enum
The _one_ {and only}
arg=two
feature=enum-feat
Also _one_ {and only}
section=None
@two is undocumented
doc symbol=Base
@ -134,11 +141,15 @@ doc symbol=Variant2
doc symbol=Object
body=
feature=union-feat1
a feature
doc symbol=SugaredUnion
body=
arg=type
feature=union-feat2
a feature
doc symbol=Alternate
body=
@ -147,6 +158,8 @@ an integer
@b is undocumented
arg=b
feature=alt-feat
a feature
doc freeform
body=
== Another subsection
@ -197,3 +210,5 @@ another feature
doc symbol=EVT-BOXED
body=
feature=feat3
a feature

View File

@ -88,6 +88,12 @@ The @emph{one} @{and only@}
@item @code{two}
Not documented
@end table
@b{Features:}
@table @asis
@item @code{enum-feat}
Also @emph{one} @{and only@}
@end table
@code{two} is undocumented
@b{If:} @code{defined(IFCOND)}
@ -151,6 +157,12 @@ a feature
@item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO})
@end table
@b{Features:}
@table @asis
@item @code{union-feat1}
a feature
@end table
@end deftp
@ -167,6 +179,12 @@ One of @t{"one"}, @t{"two"}
@item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO})
@end table
@b{Features:}
@table @asis
@item @code{union-feat2}
a feature
@end table
@end deftp
@ -184,6 +202,12 @@ an integer
Not documented
@end table
@b{Features:}
@table @asis
@item @code{alt-feat}
a feature
@end table
@end deftp
@ -283,5 +307,11 @@ another feature
@b{Arguments:} the members of @code{Object}
@b{Features:}
@table @asis
@item @code{feat3}
a feature
@end table
@end deftypefn

View File

@ -252,7 +252,7 @@
'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } },
'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
# test 'features' for structs
# test 'features'
{ 'struct': 'FeatureStruct0',
'data': { 'foo': 'int' },
@ -281,7 +281,22 @@
'data': { 'foo': 'int' },
'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)',
'defined(TEST_IF_COND_2)'] } ] }
{ 'command': 'test-features',
{ 'enum': 'FeatureEnum1',
'data': [ 'eins', 'zwei', 'drei' ],
'features': [ 'feature1' ] }
{ 'union': 'FeatureUnion1',
'base': { 'tag': 'FeatureEnum1' },
'discriminator': 'tag',
'data': { 'eins': 'FeatureStruct1' },
'features': [ 'feature1' ] }
{ 'alternate': 'FeatureAlternate1',
'data': { 'eins': 'FeatureStruct1' },
'features': [ 'feature1' ] }
{ 'command': 'test-features0',
'data': { 'fs0': 'FeatureStruct0',
'fs1': 'FeatureStruct1',
'fs2': 'FeatureStruct2',
@ -289,12 +304,9 @@
'fs4': 'FeatureStruct4',
'cfs1': 'CondFeatureStruct1',
'cfs2': 'CondFeatureStruct2',
'cfs3': 'CondFeatureStruct3' } }
# test 'features' for command
{ 'command': 'test-command-features0',
'cfs3': 'CondFeatureStruct3' },
'features': [] }
{ 'command': 'test-command-features1',
'features': [ 'feature1' ] }
{ 'command': 'test-command-features3',
@ -308,3 +320,6 @@
{ 'command': 'test-command-cond-features3',
'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)',
'defined(TEST_IF_COND_2)'] } ] }
{ 'event': 'TEST-EVENT-FEATURES1',
'features': [ 'feature1' ] }

View File

@ -387,7 +387,25 @@ object CondFeatureStruct3
member foo: int optional=False
feature feature1
if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)']
object q_obj_test-features-arg
enum FeatureEnum1
member eins
member zwei
member drei
feature feature1
object q_obj_FeatureUnion1-base
member tag: FeatureEnum1 optional=False
object FeatureUnion1
base q_obj_FeatureUnion1-base
tag tag
case eins: FeatureStruct1
case zwei: q_empty
case drei: q_empty
feature feature1
alternate FeatureAlternate1
tag type
case eins: FeatureStruct1
feature feature1
object q_obj_test-features0-arg
member fs0: FeatureStruct0 optional=False
member fs1: FeatureStruct1 optional=False
member fs2: FeatureStruct2 optional=False
@ -396,9 +414,7 @@ object q_obj_test-features-arg
member cfs1: CondFeatureStruct1 optional=False
member cfs2: CondFeatureStruct2 optional=False
member cfs3: CondFeatureStruct3 optional=False
command test-features q_obj_test-features-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
command test-command-features0 None -> None
command test-features0 q_obj_test-features0-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
command test-command-features1 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
@ -421,6 +437,9 @@ command test-command-cond-features3 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature feature1
if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)']
event TEST-EVENT-FEATURES1 None
boxed=False
feature feature1
module include/sub-module.json
include sub-sub-module.json
object SecondArrayRef

View File

@ -30,7 +30,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
def visit_include(self, name, info):
print('include %s' % name)
def visit_enum_type(self, name, info, ifcond, members, prefix):
def visit_enum_type(self, name, info, ifcond, features, members, prefix):
print('enum %s' % name)
if prefix:
print(' prefix %s' % prefix)
@ -38,6 +38,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
print(' member %s' % m.name)
self._print_if(m.ifcond, indent=8)
self._print_if(ifcond)
self._print_features(features)
def visit_array_type(self, name, info, ifcond, element_type):
if not info:
@ -58,10 +59,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_if(ifcond)
self._print_features(features)
def visit_alternate_type(self, name, info, ifcond, variants):
def visit_alternate_type(self, name, info, ifcond, features, variants):
print('alternate %s' % name)
self._print_variants(variants)
self._print_if(ifcond)
self._print_features(features)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig,
@ -74,10 +76,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_if(ifcond)
self._print_features(features)
def visit_event(self, name, info, ifcond, arg_type, boxed):
def visit_event(self, name, info, ifcond, features, arg_type, boxed):
print('event %s %s' % (name, arg_type and arg_type.name))
print(' boxed=%s' % boxed)
self._print_if(ifcond)
self._print_features(features)
@staticmethod
def _print_variants(variants):

View File

@ -45,7 +45,7 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
{
}
void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1,
void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1,
FeatureStruct2 *fs2, FeatureStruct3 *fs3,
FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1,
CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3,
@ -53,10 +53,6 @@ void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1,
{
}
void qmp_test_command_features0(Error **errp)
{
}
void qmp_test_command_features1(Error **errp)
{
}