From: Markus Armbruster <armbru@redhat.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 13/15] qapi: Don't box struct branch of alternate
Date: Fri, 19 Feb 2016 13:18:04 +0100 [thread overview]
Message-ID: <1455884286-26272-14-git-send-email-armbru@redhat.com> (raw)
In-Reply-To: <1455884286-26272-1-git-send-email-armbru@redhat.com>
From: Eric Blake <eblake@redhat.com>
There's no reason to do two malloc's for an alternate type visiting
a QAPI struct; let's just inline the struct directly as the C union
branch of the struct.
Surprisingly, no clients were actually using the struct member prior
to this patch outside of the testsuite; an earlier patch in the series
added some testsuite coverage to make the effect of this patch more
obvious.
In qapi.py, c_type() gains a new is_unboxed flag to control when we
are emitting a C struct unboxed within the context of an outer
struct (different from our other two modes of usage with no flags
for normal local variable declarations, and with is_param for adding
'const' in a parameter list). I don't know if there is any more
pythonic way of collapsing the two flags into a single parameter,
as we never have a caller setting both flags at once.
Ultimately, we want to also unbox branches for QAPI unions, but as
that touches a lot more client code, it is better as separate
patches. But since unions and alternates share gen_variants(), I
had to hack in a way to test if we are visiting an alternate type
for setting the is_unboxed flag: look for a non-object branch.
This works because alternates have at least two branches, with at
most one object branch, while unions have only object branches.
The hack will go away in a later patch.
The generated code difference to qapi-types.h is relatively small:
| struct BlockdevRef {
| QType type;
| union { /* union tag is @type */
| void *data;
|- BlockdevOptions *definition;
|+ BlockdevOptions definition;
| char *reference;
| } u;
| };
The corresponding spot in qapi-visit.c calls visit_type_FOO(), which
first calls visit_start_struct() to allocate or deallocate the member
and handle a layer of {} from the JSON stream, then visits the
members. To peel off the indirection and the memory management that
comes with it, we inline this call, then suppress allocation /
deallocation by passing NULL to visit_start_struct(), and adjust the
member visit:
| switch ((*obj)->type) {
| case QTYPE_QDICT:
|- visit_type_BlockdevOptions(v, name, &(*obj)->u.definition, &err);
|+ visit_start_struct(v, name, NULL, 0, &err);
|+ if (err) {
|+ break;
|+ }
|+ visit_type_BlockdevOptions_fields(v, &(*obj)->u.definition, &err);
|+ error_propagate(errp, err);
|+ err = NULL;
|+ visit_end_struct(v, &err);
| break;
| case QTYPE_QSTRING:
| visit_type_str(v, name, &(*obj)->u.reference, &err);
The visit of non-object fields is unchanged.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1455778109-6278-13-git-send-email-eblake@redhat.com>
[Commit message tweaked]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
scripts/qapi-types.py | 10 +++++++++-
scripts/qapi-visit.py | 33 +++++++++++++++++++++++++++------
scripts/qapi.py | 12 +++++++-----
tests/test-qmp-input-visitor.c | 20 ++++++++++----------
tests/test-qmp-output-visitor.c | 11 +++++------
5 files changed, 58 insertions(+), 28 deletions(-)
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 6ea0ae6..4dabe91 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -116,6 +116,14 @@ static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
def gen_variants(variants):
+ # HACK: Determine if this is an alternate (at least one variant
+ # is not an object); unions have all branches as objects.
+ unboxed = False
+ for v in variants.variants:
+ if not isinstance(v.type, QAPISchemaObjectType):
+ unboxed = True
+ break
+
# 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
@@ -136,7 +144,7 @@ def gen_variants(variants):
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
- c_type=typ.c_type(),
+ c_type=typ.c_type(is_unboxed=unboxed),
c_name=c_name(var.name))
ret += mcgen('''
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index d7d7ed5..f4e38d1 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -201,11 +201,14 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error
def gen_visit_alternate(name, variants):
promote_int = 'true'
+ ret = ''
for var in variants.variants:
if var.type.alternate_qtype() == 'QTYPE_QINT':
promote_int = 'false'
+ if isinstance(var.type, QAPISchemaObjectType):
+ ret += gen_visit_fields_decl(var.type)
- ret = mcgen('''
+ ret += mcgen('''
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
{
@@ -221,17 +224,35 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
}
switch ((*obj)->type) {
''',
- c_name=c_name(name), promote_int=promote_int)
+ c_name=c_name(name), promote_int=promote_int)
for var in variants.variants:
ret += mcgen('''
case %(case)s:
+''',
+ case=var.type.alternate_qtype())
+ if isinstance(var.type, QAPISchemaObjectType):
+ ret += mcgen('''
+ visit_start_struct(v, name, NULL, 0, &err);
+ if (err) {
+ break;
+ }
+ visit_type_%(c_type)s_fields(v, &(*obj)->u.%(c_name)s, &err);
+ error_propagate(errp, err);
+ err = NULL;
+ visit_end_struct(v, &err);
+''',
+ c_type=var.type.c_name(),
+ c_name=c_name(var.name))
+ else:
+ ret += mcgen('''
visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
- break;
''',
- case=var.type.alternate_qtype(),
- c_type=var.type.c_name(),
- c_name=c_name(var.name))
+ c_type=var.type.c_name(),
+ c_name=c_name(var.name))
+ ret += mcgen('''
+ break;
+''')
ret += mcgen('''
default:
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 17bf633..8497777 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -824,7 +824,7 @@ class QAPISchemaVisitor(object):
class QAPISchemaType(QAPISchemaEntity):
- def c_type(self, is_param=False):
+ def c_type(self, is_param=False, is_unboxed=False):
return c_name(self.name) + pointer_suffix
def c_null(self):
@@ -857,7 +857,7 @@ class QAPISchemaBuiltinType(QAPISchemaType):
def c_name(self):
return self.name
- def c_type(self, is_param=False):
+ def c_type(self, is_param=False, is_unboxed=False):
if is_param and self.name == 'str':
return 'const ' + self._c_type_name
return self._c_type_name
@@ -891,7 +891,7 @@ class QAPISchemaEnumType(QAPISchemaType):
# See QAPISchema._make_implicit_enum_type()
return self.name.endswith('Kind')
- def c_type(self, is_param=False):
+ def c_type(self, is_param=False, is_unboxed=False):
return c_name(self.name)
def member_names(self):
@@ -987,9 +987,11 @@ class QAPISchemaObjectType(QAPISchemaType):
assert not self.is_implicit()
return QAPISchemaType.c_name(self)
- def c_type(self, is_param=False):
+ def c_type(self, is_param=False, is_unboxed=False):
assert not self.is_implicit()
- return QAPISchemaType.c_type(self)
+ if is_unboxed:
+ return c_name(self.name)
+ return c_name(self.name) + pointer_suffix
def json_type(self):
return 'object'
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index ef836d5..47cf6aa 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -327,11 +327,11 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,
"'enum1':'value1', 'boolean':true}");
visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
g_assert_cmpint(tmp->type, ==, QTYPE_QDICT);
- g_assert_cmpint(tmp->u.udfu->integer, ==, 1);
- g_assert_cmpstr(tmp->u.udfu->string, ==, "str");
- g_assert_cmpint(tmp->u.udfu->enum1, ==, ENUM_ONE_VALUE1);
- g_assert_cmpint(tmp->u.udfu->u.value1->boolean, ==, true);
- g_assert_cmpint(tmp->u.udfu->u.value1->has_a_b, ==, false);
+ g_assert_cmpint(tmp->u.udfu.integer, ==, 1);
+ g_assert_cmpstr(tmp->u.udfu.string, ==, "str");
+ g_assert_cmpint(tmp->u.udfu.enum1, ==, ENUM_ONE_VALUE1);
+ g_assert_cmpint(tmp->u.udfu.u.value1->boolean, ==, true);
+ g_assert_cmpint(tmp->u.udfu.u.value1->has_a_b, ==, false);
qapi_free_UserDefAlternate(tmp);
v = visitor_input_test_init(data, "false");
@@ -355,11 +355,11 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,
"'enum1':'value1', 'boolean':true} }");
visit_type_WrapAlternate(v, NULL, &wrap, &error_abort);
g_assert_cmpint(wrap->alt->type, ==, QTYPE_QDICT);
- g_assert_cmpint(wrap->alt->u.udfu->integer, ==, 1);
- g_assert_cmpstr(wrap->alt->u.udfu->string, ==, "str");
- g_assert_cmpint(wrap->alt->u.udfu->enum1, ==, ENUM_ONE_VALUE1);
- g_assert_cmpint(wrap->alt->u.udfu->u.value1->boolean, ==, true);
- g_assert_cmpint(wrap->alt->u.udfu->u.value1->has_a_b, ==, false);
+ g_assert_cmpint(wrap->alt->u.udfu.integer, ==, 1);
+ g_assert_cmpstr(wrap->alt->u.udfu.string, ==, "str");
+ g_assert_cmpint(wrap->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE1);
+ g_assert_cmpint(wrap->alt->u.udfu.u.value1->boolean, ==, true);
+ g_assert_cmpint(wrap->alt->u.udfu.u.value1->has_a_b, ==, false);
qapi_free_WrapAlternate(wrap);
}
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 2b0f7e9..fe2f1a1 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -457,12 +457,11 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
tmp = g_new0(UserDefAlternate, 1);
tmp->type = QTYPE_QDICT;
- tmp->u.udfu = g_new0(UserDefFlatUnion, 1);
- tmp->u.udfu->integer = 1;
- tmp->u.udfu->string = g_strdup("str");
- tmp->u.udfu->enum1 = ENUM_ONE_VALUE1;
- tmp->u.udfu->u.value1 = g_new0(UserDefA, 1);
- tmp->u.udfu->u.value1->boolean = true;
+ tmp->u.udfu.integer = 1;
+ tmp->u.udfu.string = g_strdup("str");
+ tmp->u.udfu.enum1 = ENUM_ONE_VALUE1;
+ tmp->u.udfu.u.value1 = g_new0(UserDefA, 1);
+ tmp->u.udfu.u.value1->boolean = true;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
arg = qmp_output_get_qobject(data->qov);
--
2.4.3
next prev parent reply other threads:[~2016-02-19 12:18 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-19 12:17 [Qemu-devel] [PULL 00/15] QAPI patches for 2016-02-19 Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 01/15] qapi-visit: Honor prefix of discriminator enum Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 02/15] qapi: Simplify excess input reporting in input visitors Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 03/15] qapi: Forbid empty unions and useless alternates Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 04/15] qapi: Forbid 'any' inside an alternate Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 05/15] qapi: Add tests of complex objects within alternate Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 06/15] qapi-visit: Simplify how we visit common union members Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 07/15] qapi: Visit variants in visit_type_FOO_fields() Markus Armbruster
2016-02-19 12:17 ` [Qemu-devel] [PULL 08/15] qapi-visit: Unify struct and union visit Markus Armbruster
2016-02-19 12:18 ` [Qemu-devel] [PULL 09/15] qapi-visit: Less indirection in visit_type_Foo_fields() Markus Armbruster
2016-02-19 12:18 ` [Qemu-devel] [PULL 10/15] qapi: Adjust layout of FooList types Markus Armbruster
2016-02-19 12:18 ` [Qemu-devel] [PULL 11/15] qapi: Emit structs used as variants in topological order Markus Armbruster
2016-02-19 12:18 ` [Qemu-devel] [PULL 12/15] qapi-visit: Use common idiom in gen_visit_fields_decl() Markus Armbruster
2016-02-19 12:18 ` Markus Armbruster [this message]
2016-02-19 12:18 ` [Qemu-devel] [PULL 14/15] qapi: Don't box branches of flat unions Markus Armbruster
2016-02-23 14:50 ` Daniel P. Berrange
2016-02-23 15:18 ` Eric Blake
2016-02-23 15:30 ` Eric Blake
2016-02-23 15:33 ` Daniel P. Berrange
2016-02-19 12:18 ` [Qemu-devel] [PULL 15/15] qapi: Change visit_start_implicit_struct to visit_start_alternate Markus Armbruster
2016-02-19 15:19 ` [Qemu-devel] [PULL 00/15] QAPI patches for 2016-02-19 Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1455884286-26272-14-git-send-email-armbru@redhat.com \
--to=armbru@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).