[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 4/6] qapi: Optionally parse simple unions as flat
From: |
Kevin Wolf |
Subject: |
[PATCH v2 4/6] qapi: Optionally parse simple unions as flat |
Date: |
Fri, 23 Oct 2020 18:13:10 +0200 |
This extends the Visitor interface with an option that can enable flat
representation (without the 'data' wrapper) for simple unions. This way,
a command line parser can enable a more user friendly syntax while QMP
doesn't enable the option and uses the same representation as before.
We need to disable flat representation for ChardevSpiceChannel, which
has a 'type' option that conflicts with the ChardevBackend 'type'. This
variant will get nesting even when flat parsing is enabled in the
Visitor.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
qapi/char.json | 3 ++-
docs/devel/qapi-code-gen.txt | 11 ++++++++++-
include/qapi/visitor-impl.h | 3 +++
include/qapi/visitor.h | 10 ++++++++++
qapi/qapi-visit-core.c | 10 ++++++++++
scripts/qapi/expr.py | 7 ++++++-
scripts/qapi/schema.py | 38 ++++++++++++++++++++++++++++--------
scripts/qapi/visit.py | 20 +++++++++++++------
8 files changed, 85 insertions(+), 17 deletions(-)
diff --git a/qapi/char.json b/qapi/char.json
index 43486d1daa..57ec18220b 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -414,7 +414,8 @@
'stdio': 'ChardevStdio',
'console': 'ChardevCommon',
'spicevmc': { 'type': 'ChardevSpiceChannel',
- 'if': 'defined(CONFIG_SPICE)' },
+ 'if': 'defined(CONFIG_SPICE)',
+ 'allow-flat': false },
'spiceport': { 'type': 'ChardevSpicePort',
'if': 'defined(CONFIG_SPICE)' },
'vc': 'ChardevVC',
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 9722c1a204..ee34d39a20 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -295,7 +295,9 @@ Syntax:
'*features': FEATURES }
BRANCHES = { BRANCH, ... }
BRANCH = STRING : TYPE-REF
- | STRING : { 'type': TYPE-REF, '*if': COND }
+ | STRING : { 'type': TYPE-REF,
+ '*if': COND,
+ '*allow-flat': BOOL }
Member 'union' names the union type.
@@ -334,6 +336,13 @@ values to data types like in this example:
'data': { 'file': 'BlockdevOptionsFile',
'qcow2': 'BlockdevOptionsQcow2' } }
+Simple unions can support both wrapped and flat representation of
+branches that have a struct type, unless it is explicitly disabled in
+the schema with 'allow-flat': false. Branches of other types are
+always wrapped. Which representation is used in the generated visitor
+C code can be configured per visitor. Flat representation is
+appropriate when parsing command line options.
+
In the Client JSON Protocol, all simple union branches have wrapped
representation, as in these examples:
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 7362c043be..f628b6eb36 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -121,6 +121,9 @@ struct Visitor
/* Must be set */
void (*free)(Visitor *v);
+
+ /* Set to true to make simple unions look like flat unions */
+ bool flat_simple_unions;
};
#endif
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index ebc19ede7f..d41be4df48 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -496,6 +496,16 @@ bool visit_is_input(Visitor *v);
*/
bool visit_is_dealloc(Visitor *v);
+/*
+ * Check if simple unions should be treated as flat.
+ */
+bool visit_flat_simple_unions(Visitor *v);
+
+/*
+ * Set if simple unions should be treated as flat.
+ */
+void visit_set_flat_simple_unions(Visitor *v, bool flat);
+
/*** Visiting built-in types ***/
/*
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 7e5f40e7f0..dc6fd78b8c 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -145,6 +145,16 @@ bool visit_is_dealloc(Visitor *v)
return v->type == VISITOR_DEALLOC;
}
+bool visit_flat_simple_unions(Visitor *v)
+{
+ return v->flat_simple_unions;
+}
+
+void visit_set_flat_simple_unions(Visitor *v, bool flat)
+{
+ v->flat_simple_unions = flat;
+}
+
bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
{
assert(obj);
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
index 2fcaaa2497..a39092e4a9 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -248,7 +248,12 @@ def check_union(expr, info):
for (key, value) in members.items():
source = "'data' member '%s'" % key
check_name_str(key, info, source)
- check_keys(value, info, source, ['type'], ['if'])
+ check_keys(value, info, source, ['type'], ['if', 'allow-flat'])
+ if 'allow-flat' in value:
+ if discriminator is not None:
+ raise QAPISemError(info, "'allow-flat' requires simple union")
+ if not isinstance(value['allow-flat'], bool):
+ raise QAPISemError(info, "'allow-flat' must be a boolean")
check_if(value, info, source)
check_type(value['type'], info, source, allow_array=not base)
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 17525b4216..20a1acb10e 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -612,7 +612,18 @@ class QAPISchemaVariants:
# branch do not affect another branch. Variants that are
# never flat don't even conflict with the base.
if isinstance(v.type, QAPISchemaObjectType):
- v.type.check_clash(info, dict(seen) if v.flat else {})
+ nested_seen = dict(seen) if v.flat else {}
+ v.type.check_clash(info, nested_seen)
+
+ # Make sure that the presence of a 'data' member in
+ # some input always implies wrapped representation so
+ # that visitors can unambiguously accept both forms.
+ if v.wrapped and 'data' in nested_seen:
+ raise QAPISemError(
+ info,
+ "%s collides with flat representation of %s"
+ % (nested_seen['data'].describe(info),
+ v.describe(info)))
class QAPISchemaMember:
@@ -721,9 +732,9 @@ class QAPISchemaVariant(QAPISchemaObjectTypeMember):
self.wrapped = bool(wrapper_type)
self.wrapper_type = wrapper_type
- # For now, unions are either flat or wrapped, never both
+ # Unions that are both flat and wrapped can look like either one,
+ # depending on Visitor.flat_simple_unions
assert self.flat or self.wrapped
- assert not (self.flat and self.wrapped)
def check(self, schema):
super().check(schema)
@@ -1038,7 +1049,7 @@ class QAPISchema:
def _make_variant(self, case, typ, ifcond, info):
return QAPISchemaVariant(case, info, typ, ifcond)
- def _make_simple_variant(self, union_name, case, typ, ifcond, info):
+ def _make_simple_variant(self, union_name, case, typ, ifcond, flat, info):
if isinstance(typ, list):
assert len(typ) == 1
typ = self._make_array_type(typ[0], info)
@@ -1049,7 +1060,14 @@ class QAPISchema:
wrapper_member = self._make_member('data', typ, None, None, info)
wrapper_type = QAPISchemaObjectType(wrapper_name, info, None, ifcond,
None, None, [wrapper_member], None)
- return QAPISchemaVariant(case, info, typ, ifcond, flat=False,
+
+ # Default to allowing flat representation for object types.
+ # Other types require a wrapper, so disable flat for them by default.
+ if flat is None:
+ variant_type = self.resolve_type(typ, info, 'variant type')
+ flat = isinstance(variant_type, QAPISchemaObjectType)
+
+ return QAPISchemaVariant(case, info, typ, ifcond, flat=flat,
wrapper_type=wrapper_type)
def _def_union_type(self, expr, info, doc):
@@ -1070,9 +1088,13 @@ class QAPISchema:
for (key, value) in data.items()]
members = []
else:
- variants = [self._make_simple_variant(name, key, value['type'],
- value.get('if'), info)
- for (key, value) in data.items()]
+ variants = [
+ self._make_simple_variant(name, key, value['type'],
+ value.get('if'),
+ value.get('allow-flat'),
+ info)
+ for (key, value) in data.items()
+ ]
enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index f72567cbcc..9d05d6bd08 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -127,23 +127,31 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s
*obj, Error **errp)
case=case_str,
c_type=var.type.c_name(), c_name=c_name(var.name))
elif var.wrapped:
+ if var.flat:
+ cond = "!visit_flat_simple_unions(v)"
+ else:
+ cond = "true"
ret += mcgen('''
case %(case)s:
{
bool ok;
- if (!visit_start_struct(v, "data", NULL, 0, errp)) {
- return false;
+ if (%(cond)s) {
+ if (!visit_start_struct(v, "data", NULL, 0, errp)) {
+ return false;
+ }
}
ok = visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, errp);
- if (ok) {
- ok = visit_check_struct(v, errp);
+ if (%(cond)s) {
+ if (ok) {
+ ok = visit_check_struct(v, errp);
+ }
+ visit_end_struct(v, NULL);
}
- visit_end_struct(v, NULL);
return ok;
}
''',
- case=case_str,
+ case=case_str, cond=cond,
c_type=var.type.c_name(), c_name=c_name(var.name))
else:
--
2.28.0
- [PATCH v2 0/6] qemu-storage-daemon: QAPIfy --chardev, Kevin Wolf, 2020/10/23
- [PATCH v2 2/6] char: Factor out qemu_chr_print_types(), Kevin Wolf, 2020/10/23
- [PATCH v2 1/6] char/stdio: Fix QMP default for 'signal', Kevin Wolf, 2020/10/23
- [PATCH v2 4/6] qapi: Optionally parse simple unions as flat,
Kevin Wolf <=
- [PATCH v2 5/6] tests/qapi-schema: Flat representation of simple unions, Kevin Wolf, 2020/10/23
- [PATCH v2 6/6] qemu-storage-daemon: Use qmp_chardev_add() for --chardev, Kevin Wolf, 2020/10/23
- [PATCH v2 3/6] qapi: Remove wrapper struct for simple unions, Kevin Wolf, 2020/10/23