Skip to content

Commit

Permalink
qapi: Unbox base members
Browse files Browse the repository at this point in the history
Rather than storing a base class as a pointer to a box, just
store the fields of that base class in the same order, so that
a child struct can be directly cast to its parent.  This gives
less malloc overhead, less pointer dereferencing, and even less
generated code.  Compare to the earlier commit 1e6c161 "qapi:
Generate a nicer struct for flat unions" (although that patch
had fewer places to change, as less of qemu was directly using
qapi structs for flat unions).  It also allows us to turn on
automatic type-safe wrappers for upcasting to the base class
of a struct.

Changes to the generated code look like this in qapi-types.h:

| struct SpiceChannel {
|-    SpiceBasicInfo *base;
|+    /* Members inherited from SpiceBasicInfo: */
|+    char *host;
|+    char *port;
|+    NetworkAddressFamily family;
|+    /* Own members: */
|     int64_t connection_id;

as well as additional upcast functions like qapi_SpiceChannel_base().
Meanwhile, changes to qapi-visit.c look like:

| static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp)
| {
|     Error *err = NULL;
|
|-    visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err);
|+    visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err);
|     if (err) {

(the cast is necessary, since our upcast wrappers only deal with a
single pointer, not pointer-to-pointer); plus the wholesale
elimination of some now-unused visit_type_implicit_FOO() functions.

Without boxing, the corner case of one empty struct having
another empty struct as its base type now requires inserting a
dummy member (previously, the 'Base *base' member sufficed).

And now that we no longer consume a 'base' member in the generated
C struct, we can delete the former negative struct-base-clash-base
test.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1445898903-12082-11-git-send-email-eblake@redhat.com>
[Commit message tweaked slightly]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
  • Loading branch information
ebblake authored and Markus Armbruster committed Nov 2, 2015
1 parent 30594fe commit ddf2190
Show file tree
Hide file tree
Showing 15 changed files with 54 additions and 87 deletions.
6 changes: 3 additions & 3 deletions hmp.c
Expand Up @@ -569,8 +569,8 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
for (client = info->clients; client; client = client->next) {
monitor_printf(mon, "Client:\n");
monitor_printf(mon, " address: %s:%s\n",
client->value->base->host,
client->value->base->service);
client->value->host,
client->value->service);
monitor_printf(mon, " x509_dname: %s\n",
client->value->x509_dname ?
client->value->x509_dname : "none");
Expand Down Expand Up @@ -638,7 +638,7 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict)
for (chan = info->channels; chan; chan = chan->next) {
monitor_printf(mon, "Channel:\n");
monitor_printf(mon, " address: %s:%s%s\n",
chan->value->base->host, chan->value->base->port,
chan->value->host, chan->value->port,
chan->value->tls ? " [tls]" : "");
monitor_printf(mon, " session: %" PRId64 "\n",
chan->value->connection_id);
Expand Down
12 changes: 4 additions & 8 deletions scripts/qapi-types.py
Expand Up @@ -77,16 +77,13 @@ def gen_struct(name, base, members):
''',
c_name=c_name(name))

if base:
ret += gen_struct_field('base', base, False)

ret += gen_struct_fields(members)
ret += gen_struct_fields(members, base)

# 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:
if not (base and base.members) and not members:
ret += mcgen('''
char qapi_dummy_field_for_empty_struct;
''')
Expand Down Expand Up @@ -280,11 +277,10 @@ def visit_object_type(self, name, info, base, members, variants):
if variants:
assert not members # not implemented
self.decl += gen_union(name, base, variants)
# TODO Use gen_upcast on structs too, once they have sane layout
if base:
self.decl += gen_upcast(name, base)
else:
self.decl += gen_struct(name, base, members)
if base:
self.decl += gen_upcast(name, base)
self._gen_type_cleanup(name)

def visit_alternate_type(self, name, info, variants):
Expand Down
9 changes: 4 additions & 5 deletions scripts/qapi-visit.py
Expand Up @@ -72,13 +72,12 @@ def gen_visit_implicit_struct(typ):


def gen_visit_struct_fields(name, base, members):
struct_fields_seen.add(name)

ret = ''

if base:
ret += gen_visit_implicit_struct(base)
ret += gen_visit_fields_decl(base)

struct_fields_seen.add(name)
ret += mcgen('''
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
Expand All @@ -90,9 +89,9 @@ def gen_visit_struct_fields(name, base, members):

if base:
ret += mcgen('''
visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
''',
c_type=base.c_name(), c_name=c_name('base'))
c_type=base.c_name())
ret += gen_err_check()

ret += gen_visit_fields(members, prefix='(*obj)->')
Expand Down
1 change: 0 additions & 1 deletion tests/Makefile
Expand Up @@ -325,7 +325,6 @@ qapi-schema += returns-array-bad.json
qapi-schema += returns-dict.json
qapi-schema += returns-unknown.json
qapi-schema += returns-whitelist.json
qapi-schema += struct-base-clash-base.json
qapi-schema += struct-base-clash-deep.json
qapi-schema += struct-base-clash.json
qapi-schema += struct-data-invalid.json
Expand Down
Empty file.
1 change: 0 additions & 1 deletion tests/qapi-schema/struct-base-clash-base.exit

This file was deleted.

9 changes: 0 additions & 9 deletions tests/qapi-schema/struct-base-clash-base.json

This file was deleted.

5 changes: 0 additions & 5 deletions tests/qapi-schema/struct-base-clash-base.out

This file was deleted.

15 changes: 5 additions & 10 deletions tests/test-qmp-commands.c
Expand Up @@ -25,11 +25,9 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a,
UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne));

ud1c->string = strdup(ud1a->string);
ud1c->base = g_new0(UserDefZero, 1);
ud1c->base->integer = ud1a->base->integer;
ud1c->integer = ud1a->integer;
ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0");
ud1d->base = g_new0(UserDefZero, 1);
ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0;
ud1d->integer = has_udb1 ? ud1b->integer : 0;

ret = g_new0(UserDefTwo, 1);
ret->string0 = strdup("blah1");
Expand Down Expand Up @@ -176,20 +174,17 @@ static void test_dealloc_types(void)
UserDefOneList *ud1list;

ud1test = g_malloc0(sizeof(UserDefOne));
ud1test->base = g_new0(UserDefZero, 1);
ud1test->base->integer = 42;
ud1test->integer = 42;
ud1test->string = g_strdup("hi there 42");

qapi_free_UserDefOne(ud1test);

ud1a = g_malloc0(sizeof(UserDefOne));
ud1a->base = g_new0(UserDefZero, 1);
ud1a->base->integer = 43;
ud1a->integer = 43;
ud1a->string = g_strdup("hi there 43");

ud1b = g_malloc0(sizeof(UserDefOne));
ud1b->base = g_new0(UserDefZero, 1);
ud1b->base->integer = 44;
ud1b->integer = 44;
ud1b->string = g_strdup("hi there 44");

ud1list = g_malloc0(sizeof(UserDefOneList));
Expand Down
8 changes: 2 additions & 6 deletions tests/test-qmp-event.c
Expand Up @@ -179,9 +179,7 @@ static void test_event_c(TestEventData *data,
QDict *d, *d_data, *d_b;

UserDefOne b;
UserDefZero z;
z.integer = 2;
b.base = &z;
b.integer = 2;
b.string = g_strdup("test1");
b.has_enum1 = false;

Expand Down Expand Up @@ -209,11 +207,9 @@ static void test_event_d(TestEventData *data,
{
UserDefOne struct1;
EventStructOne a;
UserDefZero z;
QDict *d, *d_data, *d_a, *d_struct1;

z.integer = 2;
struct1.base = &z;
struct1.integer = 2;
struct1.string = g_strdup("test1");
struct1.has_enum1 = true;
struct1.enum1 = ENUM_ONE_VALUE1;
Expand Down
4 changes: 2 additions & 2 deletions tests/test-qmp-input-visitor.c
Expand Up @@ -262,7 +262,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data,

check_and_free_str(udp->string0, "string0");
check_and_free_str(udp->dict1->string1, "string1");
g_assert_cmpint(udp->dict1->dict2->userdef->base->integer, ==, 42);
g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42);
check_and_free_str(udp->dict1->dict2->userdef->string, "string");
check_and_free_str(udp->dict1->dict2->string, "string2");
g_assert(udp->dict1->has_dict3 == false);
Expand Down Expand Up @@ -292,7 +292,7 @@ static void test_visitor_in_list(TestInputVisitorData *data,

snprintf(string, sizeof(string), "string%d", i);
g_assert_cmpstr(item->value->string, ==, string);
g_assert_cmpint(item->value->base->integer, ==, 42 + i);
g_assert_cmpint(item->value->integer, ==, 42 + i);
}

qapi_free_UserDefOneList(head);
Expand Down
13 changes: 5 additions & 8 deletions tests/test-qmp-output-visitor.c
Expand Up @@ -250,16 +250,14 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
ud2->dict1->dict2->userdef->string = g_strdup(string);
ud2->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
ud2->dict1->dict2->userdef->base->integer = value;
ud2->dict1->dict2->userdef->integer = value;
ud2->dict1->dict2->string = g_strdup(strings[2]);

ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
ud2->dict1->has_dict3 = true;
ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
ud2->dict1->dict3->userdef->string = g_strdup(string);
ud2->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
ud2->dict1->dict3->userdef->base->integer = value;
ud2->dict1->dict3->userdef->integer = value;
ud2->dict1->dict3->string = g_strdup(strings[3]);

visit_type_UserDefTwo(data->ov, &ud2, "unused", &err);
Expand Down Expand Up @@ -301,8 +299,8 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
const void *unused)
{
EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
UserDefZero b;
UserDefOne u = { .base = &b }, *pu = &u;
UserDefOne u = {0};
UserDefOne *pu = &u;
Error *err;
int i;

Expand Down Expand Up @@ -416,8 +414,7 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
p->value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
p->value->dict1->dict2->userdef = g_new0(UserDefOne, 1);
p->value->dict1->dict2->userdef->string = g_strdup(string);
p->value->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
p->value->dict1->dict2->userdef->base->integer = 42;
p->value->dict1->dict2->userdef->integer = 42;
p->value->dict1->dict2->string = g_strdup(string);
p->value->dict1->has_dict3 = false;

Expand Down
14 changes: 6 additions & 8 deletions tests/test-visitor-serialization.c
Expand Up @@ -258,15 +258,13 @@ static UserDefTwo *nested_struct_create(void)
udnp->dict1->string1 = strdup("test_string1");
udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2));
udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1);
udnp->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
udnp->dict1->dict2->userdef->base->integer = 42;
udnp->dict1->dict2->userdef->integer = 42;
udnp->dict1->dict2->userdef->string = strdup("test_string");
udnp->dict1->dict2->string = strdup("test_string2");
udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3));
udnp->dict1->has_dict3 = true;
udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1);
udnp->dict1->dict3->userdef->base = g_new0(UserDefZero, 1);
udnp->dict1->dict3->userdef->base->integer = 43;
udnp->dict1->dict3->userdef->integer = 43;
udnp->dict1->dict3->userdef->string = strdup("test_string");
udnp->dict1->dict3->string = strdup("test_string3");
return udnp;
Expand All @@ -278,15 +276,15 @@ static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2)
g_assert(udnp2);
g_assert_cmpstr(udnp1->string0, ==, udnp2->string0);
g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1);
g_assert_cmpint(udnp1->dict1->dict2->userdef->base->integer, ==,
udnp2->dict1->dict2->userdef->base->integer);
g_assert_cmpint(udnp1->dict1->dict2->userdef->integer, ==,
udnp2->dict1->dict2->userdef->integer);
g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==,
udnp2->dict1->dict2->userdef->string);
g_assert_cmpstr(udnp1->dict1->dict2->string, ==,
udnp2->dict1->dict2->string);
g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3);
g_assert_cmpint(udnp1->dict1->dict3->userdef->base->integer, ==,
udnp2->dict1->dict3->userdef->base->integer);
g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==,
udnp2->dict1->dict3->userdef->integer);
g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==,
udnp2->dict1->dict3->userdef->string);
g_assert_cmpstr(udnp1->dict1->dict3->string, ==,
Expand Down
23 changes: 13 additions & 10 deletions ui/spice-core.c
Expand Up @@ -200,8 +200,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
{
SpiceServerInfo *server = g_malloc0(sizeof(*server));
SpiceChannel *client = g_malloc0(sizeof(*client));
server->base = g_malloc0(sizeof(*server->base));
client->base = g_malloc0(sizeof(*client->base));

/*
* Spice server might have called us from spice worker thread
Expand All @@ -218,9 +216,11 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
}

if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
add_addr_info(qapi_SpiceChannel_base(client),
(struct sockaddr *)&info->paddr_ext,
info->plen_ext);
add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext,
add_addr_info(qapi_SpiceServerInfo_base(server),
(struct sockaddr *)&info->laddr_ext,
info->llen_ext);
} else {
error_report("spice: %s, extended address is expected",
Expand All @@ -229,7 +229,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)

switch (event) {
case SPICE_CHANNEL_EVENT_CONNECTED:
qapi_event_send_spice_connected(server->base, client->base, &error_abort);
qapi_event_send_spice_connected(qapi_SpiceServerInfo_base(server),
qapi_SpiceChannel_base(client),
&error_abort);
break;
case SPICE_CHANNEL_EVENT_INITIALIZED:
if (auth) {
Expand All @@ -242,7 +244,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
break;
case SPICE_CHANNEL_EVENT_DISCONNECTED:
channel_list_del(info);
qapi_event_send_spice_disconnected(server->base, client->base, &error_abort);
qapi_event_send_spice_disconnected(qapi_SpiceServerInfo_base(server),
qapi_SpiceChannel_base(client),
&error_abort);
break;
default:
break;
Expand Down Expand Up @@ -378,16 +382,15 @@ static SpiceChannelList *qmp_query_spice_channels(void)

chan = g_malloc0(sizeof(*chan));
chan->value = g_malloc0(sizeof(*chan->value));
chan->value->base = g_malloc0(sizeof(*chan->value->base));

paddr = (struct sockaddr *)&item->info->paddr_ext;
plen = item->info->plen_ext;
getnameinfo(paddr, plen,
host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
chan->value->base->host = g_strdup(host);
chan->value->base->port = g_strdup(port);
chan->value->base->family = inet_netfamily(paddr->sa_family);
chan->value->host = g_strdup(host);
chan->value->port = g_strdup(port);
chan->value->family = inet_netfamily(paddr->sa_family);

chan->value->connection_id = item->info->connection_id;
chan->value->channel_type = item->info->type;
Expand Down
21 changes: 10 additions & 11 deletions ui/vnc.c
Expand Up @@ -262,8 +262,8 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
Error *err = NULL;

info = g_malloc(sizeof(*info));
info->base = g_malloc(sizeof(*info->base));
vnc_init_basic_info_from_server_addr(vd->lsock, info->base, &err);
vnc_init_basic_info_from_server_addr(vd->lsock,
qapi_VncServerInfo_base(info), &err);
info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
if (err) {
Expand Down Expand Up @@ -300,8 +300,8 @@ static void vnc_client_cache_addr(VncState *client)
Error *err = NULL;

client->info = g_malloc0(sizeof(*client->info));
client->info->base = g_malloc0(sizeof(*client->info->base));
vnc_init_basic_info_from_remote_addr(client->csock, client->info->base,
vnc_init_basic_info_from_remote_addr(client->csock,
qapi_VncClientInfo_base(client->info),
&err);
if (err) {
qapi_free_VncClientInfo(client->info);
Expand All @@ -317,7 +317,6 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
if (!vs->info) {
return;
}
g_assert(vs->info->base);

si = vnc_server_info_get(vs->vd);
if (!si) {
Expand All @@ -326,7 +325,8 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)

switch (event) {
case QAPI_EVENT_VNC_CONNECTED:
qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info),
&error_abort);
break;
case QAPI_EVENT_VNC_INITIALIZED:
qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
Expand Down Expand Up @@ -361,11 +361,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
}

info = g_malloc0(sizeof(*info));
info->base = g_malloc0(sizeof(*info->base));
info->base->host = g_strdup(host);
info->base->service = g_strdup(serv);
info->base->family = inet_netfamily(sa.ss_family);
info->base->websocket = client->websocket;
info->host = g_strdup(host);
info->service = g_strdup(serv);
info->family = inet_netfamily(sa.ss_family);
info->websocket = client->websocket;

if (client->tls) {
info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
Expand Down

0 comments on commit ddf2190

Please sign in to comment.