From 2ea1793bd90f04c34fbb75a1b84d71cb5b1f9c08 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 22 Oct 2015 11:25:43 +0100 Subject: [PATCH 01/25] qapi-schema: mark InetSocketAddress as mandatory again Revert the qapi-schema.json change done in: commit 0983f5e6af76d5df8c6346cbdfff9d8305fb6da0 Author: Daniel P. Berrange Date: Tue Sep 1 14:46:50 2015 +0100 sockets: allow port to be NULL when listening on IP address Switching "port" from mandatory to optional causes the QAPI code generator to add a 'has_port' field to the InetSocketAddress struct. No code that created InetSocketAddress objects was updated to set 'has_port = true', which caused the non-NULL port strings to be silently dropped when copying InetSocketAddress objects. Reported-by: Knut Omang Signed-off-by: Daniel P. Berrange Message-Id: <1445509543-30679-1-git-send-email-berrange@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Markus Armbruster --- qapi-schema.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qapi-schema.json b/qapi-schema.json index f60be2950c..702b7b5dbd 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2614,9 +2614,7 @@ # # @host: host part of the address # -# @port: port part of the address, or lowest port if @to is present. -# Kernel selects a free port if omitted for listener addresses. -# #optional +# @port: port part of the address, or lowest port if @to is present # # @to: highest port to try # @@ -2631,7 +2629,7 @@ { 'struct': 'InetSocketAddress', 'data': { 'host': 'str', - '*port': 'str', + 'port': 'str', '*to': 'uint16', '*ipv4': 'bool', '*ipv6': 'bool' } } From 1976708321f21ed51d0a374db6b28a6cd1bd5d66 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:40 -0600 Subject: [PATCH 02/25] tests/qapi-schema: Test for reserved names, empty struct Add some testsuite coverage to ensure future patches are on the right track: Our current C representation of qapi arrays is done by appending 'List' to the element name; but we are not preventing the creation of an object type with the same name. Add reserved-type-list.json to test this. Then rename enum-union-clash.json to reserved-type-kind.json to cover the reservation that we DO detect, and shorten it to match the fact that the name is reserved even if there is no clash. We are failing to detect a collision between a dictionary member and the implicit 'has_*' flag for another optional member. The easiest fix would be for a future patch to reserve the entire "has[-_]" namespace for member names (the collision is also possible for branch names within flat unions, but only as long as branch names can collide with (non-variant) members; however, since future patches are about to remove that, it is not worth testing here). Add reserved-member-has.json to test this. A similar collision exists between a dictionary member where c_name() munges what might otherwise be a reserved name to start with 'q_', and another member explicitly starts with "q[-_]". Again, the easiest solution for a future patch will be reserving the entire namespace, but here for commands as well as members. Add reserved-member-q.json and reserved-command-q.json to test this; separate tests since arguably our munging of command 'unix' to 'qmp_q_unix()' could be done without a q_, which is different than the munging of a member 'unix' to 'foo.q_unix'. Finally, our testsuite does not have any compilation coverage of struct inheritance with empty qapi structs. Update qapi-schema-test.json to test this. Note that there is currently no technical reason to forbid type name patterns from member names, or member name patterns from types, since the two are not in the same namespace in C and won't collide; but it's not worth adding positive tests of these corner cases at this time, especially while there is other churn pending in patches that rearrange which collisions actually happen. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-2-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- tests/Makefile | 6 +++++- tests/qapi-schema/enum-union-clash.err | 1 - tests/qapi-schema/qapi-schema-test.json | 4 ++++ tests/qapi-schema/qapi-schema-test.out | 3 +++ .../{enum-union-clash.out => reserved-command-q.err} | 0 tests/qapi-schema/reserved-command-q.exit | 1 + tests/qapi-schema/reserved-command-q.json | 7 +++++++ tests/qapi-schema/reserved-command-q.out | 5 +++++ tests/qapi-schema/reserved-member-has.err | 0 tests/qapi-schema/reserved-member-has.exit | 1 + tests/qapi-schema/reserved-member-has.json | 6 ++++++ tests/qapi-schema/reserved-member-has.out | 6 ++++++ tests/qapi-schema/reserved-member-q.err | 0 tests/qapi-schema/reserved-member-q.exit | 1 + tests/qapi-schema/reserved-member-q.json | 6 ++++++ tests/qapi-schema/reserved-member-q.out | 4 ++++ tests/qapi-schema/reserved-type-kind.err | 1 + .../{enum-union-clash.exit => reserved-type-kind.exit} | 0 .../{enum-union-clash.json => reserved-type-kind.json} | 2 -- tests/qapi-schema/reserved-type-kind.out | 0 tests/qapi-schema/reserved-type-list.err | 0 tests/qapi-schema/reserved-type-list.exit | 1 + tests/qapi-schema/reserved-type-list.json | 5 +++++ tests/qapi-schema/reserved-type-list.out | 3 +++ 24 files changed, 59 insertions(+), 4 deletions(-) delete mode 100644 tests/qapi-schema/enum-union-clash.err rename tests/qapi-schema/{enum-union-clash.out => reserved-command-q.err} (100%) create mode 100644 tests/qapi-schema/reserved-command-q.exit create mode 100644 tests/qapi-schema/reserved-command-q.json create mode 100644 tests/qapi-schema/reserved-command-q.out create mode 100644 tests/qapi-schema/reserved-member-has.err create mode 100644 tests/qapi-schema/reserved-member-has.exit create mode 100644 tests/qapi-schema/reserved-member-has.json create mode 100644 tests/qapi-schema/reserved-member-has.out create mode 100644 tests/qapi-schema/reserved-member-q.err create mode 100644 tests/qapi-schema/reserved-member-q.exit create mode 100644 tests/qapi-schema/reserved-member-q.json create mode 100644 tests/qapi-schema/reserved-member-q.out create mode 100644 tests/qapi-schema/reserved-type-kind.err rename tests/qapi-schema/{enum-union-clash.exit => reserved-type-kind.exit} (100%) rename tests/qapi-schema/{enum-union-clash.json => reserved-type-kind.json} (69%) create mode 100644 tests/qapi-schema/reserved-type-kind.out create mode 100644 tests/qapi-schema/reserved-type-list.err create mode 100644 tests/qapi-schema/reserved-type-list.exit create mode 100644 tests/qapi-schema/reserved-type-list.json create mode 100644 tests/qapi-schema/reserved-type-list.out diff --git a/tests/Makefile b/tests/Makefile index 0739bfe1bf..652294cccd 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -265,7 +265,6 @@ qapi-schema += enum-dict-member.json qapi-schema += enum-int-member.json qapi-schema += enum-max-member.json qapi-schema += enum-missing-data.json -qapi-schema += enum-union-clash.json qapi-schema += enum-wrong-data.json qapi-schema += escape-outside-string.json qapi-schema += escape-too-big.json @@ -316,6 +315,11 @@ qapi-schema += redefined-builtin.json qapi-schema += redefined-command.json qapi-schema += redefined-event.json qapi-schema += redefined-type.json +qapi-schema += reserved-command-q.json +qapi-schema += reserved-member-has.json +qapi-schema += reserved-member-q.json +qapi-schema += reserved-type-kind.json +qapi-schema += reserved-type-list.json qapi-schema += returns-alternate.json qapi-schema += returns-array-bad.json qapi-schema += returns-dict.json diff --git a/tests/qapi-schema/enum-union-clash.err b/tests/qapi-schema/enum-union-clash.err deleted file mode 100644 index c04e1a8064..0000000000 --- a/tests/qapi-schema/enum-union-clash.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/enum-union-clash.json:2: enum 'UnionKind' should not end in 'Kind' diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 4e2d7c2063..48e104ba13 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -11,6 +11,10 @@ # An empty enum, although unusual, is currently acceptable { 'enum': 'MyEnum', 'data': [ ] } +# Likewise for an empty struct, including an empty base +{ 'struct': 'Empty1', 'data': { } } +{ 'struct': 'Empty2', 'base': 'Empty1', 'data': { } } + # for testing override of default naming heuristic { 'enum': 'QEnumTwo', 'prefix': 'QENUM_TWO', diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index a6c80e04d7..a7e9aabec0 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -81,6 +81,9 @@ event EVENT_A None event EVENT_B None event EVENT_C :obj-EVENT_C-arg event EVENT_D :obj-EVENT_D-arg +object Empty1 +object Empty2 + base Empty1 enum EnumOne ['value1', 'value2', 'value3'] object EventStructOne member struct1: UserDefOne optional=False diff --git a/tests/qapi-schema/enum-union-clash.out b/tests/qapi-schema/reserved-command-q.err similarity index 100% rename from tests/qapi-schema/enum-union-clash.out rename to tests/qapi-schema/reserved-command-q.err diff --git a/tests/qapi-schema/reserved-command-q.exit b/tests/qapi-schema/reserved-command-q.exit new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/tests/qapi-schema/reserved-command-q.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/reserved-command-q.json b/tests/qapi-schema/reserved-command-q.json new file mode 100644 index 0000000000..be9944c68a --- /dev/null +++ b/tests/qapi-schema/reserved-command-q.json @@ -0,0 +1,7 @@ +# C entity name collision +# FIXME - This parses, but fails to compile, because it attempts to declare +# two 'qmp_q_unix' functions (one for 'q-unix', the other because c_name() +# munges 'unix' to 'q_unix' to avoid reserved word collisions). We should +# reject attempts to explicitly use 'q_' names, to reserve it for qapi. +{ 'command': 'unix' } +{ 'command': 'q-unix' } diff --git a/tests/qapi-schema/reserved-command-q.out b/tests/qapi-schema/reserved-command-q.out new file mode 100644 index 0000000000..b31b38ff0d --- /dev/null +++ b/tests/qapi-schema/reserved-command-q.out @@ -0,0 +1,5 @@ +object :empty +command q-unix None -> None + gen=True success_response=True +command unix None -> None + gen=True success_response=True diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/reserved-member-has.exit b/tests/qapi-schema/reserved-member-has.exit new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/tests/qapi-schema/reserved-member-has.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/reserved-member-has.json b/tests/qapi-schema/reserved-member-has.json new file mode 100644 index 0000000000..a2197de6b5 --- /dev/null +++ b/tests/qapi-schema/reserved-member-has.json @@ -0,0 +1,6 @@ +# C member name collision +# FIXME - This parses, but fails to compile, because the C struct is given +# two 'has_a' members, one from the flag for optional 'a', and the other +# from member 'has-a'. Either reject this at parse time, or munge the C +# names to avoid the collision. +{ 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } } diff --git a/tests/qapi-schema/reserved-member-has.out b/tests/qapi-schema/reserved-member-has.out new file mode 100644 index 0000000000..5a18b6be8c --- /dev/null +++ b/tests/qapi-schema/reserved-member-has.out @@ -0,0 +1,6 @@ +object :empty +object :obj-oops-arg + member a: str optional=True + member has-a: str optional=False +command oops :obj-oops-arg -> None + gen=True success_response=True diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/reserved-member-q.exit b/tests/qapi-schema/reserved-member-q.exit new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/tests/qapi-schema/reserved-member-q.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/reserved-member-q.json b/tests/qapi-schema/reserved-member-q.json new file mode 100644 index 0000000000..1602ed3281 --- /dev/null +++ b/tests/qapi-schema/reserved-member-q.json @@ -0,0 +1,6 @@ +# C member name collision +# FIXME - This parses, but fails to compile, because it attempts to declare +# two 'q_unix' members (one for 'q-unix', the other because c_name() +# munges 'unix' to 'q_unix' to avoid reserved word collisions). We should +# reject attempts to explicitly use 'q_' names, to reserve it for qapi. +{ 'struct': 'Foo', 'data': { 'unix':'int', 'q-unix':'bool' } } diff --git a/tests/qapi-schema/reserved-member-q.out b/tests/qapi-schema/reserved-member-q.out new file mode 100644 index 0000000000..0d8685aeb0 --- /dev/null +++ b/tests/qapi-schema/reserved-member-q.out @@ -0,0 +1,4 @@ +object :empty +object Foo + member unix: int optional=False + member q-unix: bool optional=False diff --git a/tests/qapi-schema/reserved-type-kind.err b/tests/qapi-schema/reserved-type-kind.err new file mode 100644 index 0000000000..0a38efaad8 --- /dev/null +++ b/tests/qapi-schema/reserved-type-kind.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-type-kind.json:2: enum 'UnionKind' should not end in 'Kind' diff --git a/tests/qapi-schema/enum-union-clash.exit b/tests/qapi-schema/reserved-type-kind.exit similarity index 100% rename from tests/qapi-schema/enum-union-clash.exit rename to tests/qapi-schema/reserved-type-kind.exit diff --git a/tests/qapi-schema/enum-union-clash.json b/tests/qapi-schema/reserved-type-kind.json similarity index 69% rename from tests/qapi-schema/enum-union-clash.json rename to tests/qapi-schema/reserved-type-kind.json index 593282b6cf..9ecaba12bc 100644 --- a/tests/qapi-schema/enum-union-clash.json +++ b/tests/qapi-schema/reserved-type-kind.json @@ -1,4 +1,2 @@ # we reject types that would conflict with implicit union enum { 'enum': 'UnionKind', 'data': [ 'oops' ] } -{ 'union': 'Union', - 'data': { 'a': 'int' } } diff --git a/tests/qapi-schema/reserved-type-kind.out b/tests/qapi-schema/reserved-type-kind.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/reserved-type-list.err b/tests/qapi-schema/reserved-type-list.err new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/reserved-type-list.exit b/tests/qapi-schema/reserved-type-list.exit new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/tests/qapi-schema/reserved-type-list.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/reserved-type-list.json b/tests/qapi-schema/reserved-type-list.json new file mode 100644 index 0000000000..5b7d0f995f --- /dev/null +++ b/tests/qapi-schema/reserved-type-list.json @@ -0,0 +1,5 @@ +# Potential C name collision +# FIXME - This parses and compiles on its own, but prevents the user from +# creating a type named 'Foo' and using ['Foo'] for an array. We should +# reject the use of any type names ending in 'List'. +{ 'struct': 'FooList', 'data': { 's': 'str' } } diff --git a/tests/qapi-schema/reserved-type-list.out b/tests/qapi-schema/reserved-type-list.out new file mode 100644 index 0000000000..0406bfe319 --- /dev/null +++ b/tests/qapi-schema/reserved-type-list.out @@ -0,0 +1,3 @@ +object :empty +object FooList + member s: str optional=False From 8712fa5333ad348da20034b717dd814219d1ec11 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:41 -0600 Subject: [PATCH 03/25] qapi: More idiomatic string operations Rather than slicing the end of a string, we can use python's endswith(). And rather than creating a set of characters, we can search for a character within a string. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-3-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/qapi.py b/scripts/qapi.py index 9d53255320..3af4c2c737 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -172,7 +172,7 @@ class QAPISchemaParser(object): if self.tok == '#': self.cursor = self.src.find('\n', self.cursor) - elif self.tok in ['{', '}', ':', ',', '[', ']']: + elif self.tok in "{}:,[]": return elif self.tok == "'": string = '' @@ -390,7 +390,7 @@ def add_name(name, info, meta, implicit=False): raise QAPIExprError(info, "%s '%s' is already defined" % (all_names[name], name)) - if not implicit and name[-4:] == 'Kind': + if not implicit and name.endswith('Kind'): raise QAPIExprError(info, "%s '%s' should not end in 'Kind'" % (meta, name)) @@ -910,7 +910,7 @@ class QAPISchemaEnumType(QAPISchemaType): def is_implicit(self): # See QAPISchema._make_implicit_enum_type() - return self.name[-4:] == 'Kind' + return self.name.endswith('Kind') def c_type(self, is_param=False): return c_name(self.name) From f9e6102b48f21e464a847a858a456c521e7a83e5 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:42 -0600 Subject: [PATCH 04/25] qapi: More robust conditions for when labels are needed We were using regular expressions to see if ret included any earlier text that emitted a 'goto out;' line, to decide whether we needed to output an 'out:' label. But this is fragile, if the ret text can possibly combine more than one generated function body, where the first function used a goto but the second does not. Change the code to just check for the known conditions which cause an error check to be needed. Besides, it's slightly more efficient to use plain checks than regular expression searching. No change to generated code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-4-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi-commands.py | 4 +++- scripts/qapi-visit.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 43a893b4eb..561e47a42b 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -175,7 +175,9 @@ def gen_marshal(name, arg_type, ret_type): ret += gen_marshal_input_visit(arg_type) ret += gen_call(name, arg_type, ret_type) - if re.search('^ *goto out;', ret, re.MULTILINE): + # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields() + # for each arg_type member, and by gen_call() for ret_type + if (arg_type and arg_type.members) or ret_type: ret += mcgen(''' out: diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index d0759d739a..2dc3aed220 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -87,7 +87,8 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e ret += gen_visit_fields(members, prefix='(*obj)->') - if re.search('^ *goto out;', ret, re.MULTILINE): + # 'goto out' produced for base, and by gen_visit_fields() for each member + if base or members: ret += mcgen(''' out: From 255960dd374d4497d6ea537305f1b0d8a3433789 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:43 -0600 Subject: [PATCH 05/25] qapi: Reserve '*List' type names for list types Type names ending in 'List' can clash with qapi list types in generated C. We don't currently use such names. It is easier to outlaw them now than to worry about how to resolve such a clash in the future. For precedence, see commit 4dc2e69, which did the same for names ending in 'Kind' versus implicit enum types for qapi unions. Update the testsuite to match. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-5-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- docs/qapi-code-gen.txt | 3 ++- scripts/qapi.py | 10 ++++------ tests/qapi-schema/reserved-type-list.err | 1 + tests/qapi-schema/reserved-type-list.exit | 2 +- tests/qapi-schema/reserved-type-list.json | 6 +++--- tests/qapi-schema/reserved-type-list.out | 3 --- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 2afab20f55..c4264a819b 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -106,7 +106,8 @@ Types, commands, and events share a common namespace. Therefore, generally speaking, type definitions should always use CamelCase for user-defined type names, while built-in types are lowercase. Type definitions should not end in 'Kind', as this namespace is used for -creating implicit C enums for visiting union types. Command names, +creating implicit C enums for visiting union types, or in 'List', as +this namespace is used for creating array types. Command names, and field names within a type, should be all lower case with words separated by a hyphen. However, some existing older commands and complex types use underscore; when extending such expressions, diff --git a/scripts/qapi.py b/scripts/qapi.py index 3af4c2c737..d53b5c4b45 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -390,10 +390,10 @@ def add_name(name, info, meta, implicit=False): raise QAPIExprError(info, "%s '%s' is already defined" % (all_names[name], name)) - if not implicit and name.endswith('Kind'): + if not implicit and (name.endswith('Kind') or name.endswith('List')): raise QAPIExprError(info, - "%s '%s' should not end in 'Kind'" - % (meta, name)) + "%s '%s' should not end in '%s'" + % (meta, name, name[-4:])) all_names[name] = meta @@ -1196,9 +1196,7 @@ class QAPISchema(object): return name def _make_array_type(self, element_type, info): - # TODO fooList namespace is not reserved; user can create collisions, - # or abuse our type system with ['fooList'] for 2D array - name = element_type + 'List' + name = element_type + 'List' # Use namespace reserved by add_name() if not self.lookup_type(name): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name diff --git a/tests/qapi-schema/reserved-type-list.err b/tests/qapi-schema/reserved-type-list.err index e69de29bb2..4510fa6d90 100644 --- a/tests/qapi-schema/reserved-type-list.err +++ b/tests/qapi-schema/reserved-type-list.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-type-list.json:5: struct 'FooList' should not end in 'List' diff --git a/tests/qapi-schema/reserved-type-list.exit b/tests/qapi-schema/reserved-type-list.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-type-list.exit +++ b/tests/qapi-schema/reserved-type-list.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-type-list.json b/tests/qapi-schema/reserved-type-list.json index 5b7d0f995f..98d53bf808 100644 --- a/tests/qapi-schema/reserved-type-list.json +++ b/tests/qapi-schema/reserved-type-list.json @@ -1,5 +1,5 @@ # Potential C name collision -# FIXME - This parses and compiles on its own, but prevents the user from -# creating a type named 'Foo' and using ['Foo'] for an array. We should -# reject the use of any type names ending in 'List'. +# We reserve names ending in 'List' for use by array types. +# TODO - we could choose array names to avoid collision with user types, +# in order to let this compile { 'struct': 'FooList', 'data': { 's': 'str' } } diff --git a/tests/qapi-schema/reserved-type-list.out b/tests/qapi-schema/reserved-type-list.out index 0406bfe319..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-type-list.out +++ b/tests/qapi-schema/reserved-type-list.out @@ -1,3 +0,0 @@ -object :empty -object FooList - member s: str optional=False From 9fb081e0b98409556d023c7193eeb68947cd1211 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:44 -0600 Subject: [PATCH 06/25] qapi: Reserve 'q_*' and 'has_*' member names c_name() produces names starting with 'q_' when protecting a dictionary member name that would fail to directly compile, but in doing so can cause clashes with any member name already beginning with 'q-' or 'q_'. Likewise, we create a C name 'has_' for any optional member that can clash with any member name beginning with 'has-' or 'has_'. Technically, rather than blindly reserving the namespace, we could try to complain about user names only when an actual collision occurs, or even teach c_name() how to munge names to avoid collisions. But it is not trivial, especially when collisions can occur across multiple types (such as via inheritance or flat unions). Besides, no existing .json files are trying to use these names. So it's easier to just outright forbid the potential for collision. We can always relax things in the future if a real need arises for QMP to express member names that have been forbidden here. 'has_' only has to be reserved for struct/union member names, while 'q_' is reserved everywhere (matching the fact that only members can be optional, while we use c_name() for munging both members and entities). Note that we could relax 'q_' restrictions on entities independently from member names; for example, c_name('qmp_' + 'unix') would result in a different function name than our current 'qmp_' + c_name('unix'). Update and add tests to cover the new error messages. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-6-git-send-email-eblake@redhat.com> [Consistently pass protect=False to c_name(); commit message tweaked slightly] Signed-off-by: Markus Armbruster --- docs/qapi-code-gen.txt | 11 +++++++---- scripts/qapi.py | 8 +++++++- tests/qapi-schema/reserved-command-q.err | 1 + tests/qapi-schema/reserved-command-q.exit | 2 +- tests/qapi-schema/reserved-command-q.json | 6 ++---- tests/qapi-schema/reserved-command-q.out | 5 ----- tests/qapi-schema/reserved-member-has.err | 1 + tests/qapi-schema/reserved-member-has.exit | 2 +- tests/qapi-schema/reserved-member-has.json | 7 +++---- tests/qapi-schema/reserved-member-has.out | 6 ------ tests/qapi-schema/reserved-member-q.err | 1 + tests/qapi-schema/reserved-member-q.exit | 2 +- tests/qapi-schema/reserved-member-q.json | 6 ++---- tests/qapi-schema/reserved-member-q.out | 4 ---- 14 files changed, 27 insertions(+), 35 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index c4264a819b..4d8c2fcf02 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -112,7 +112,9 @@ and field names within a type, should be all lower case with words separated by a hyphen. However, some existing older commands and complex types use underscore; when extending such expressions, consistency is preferred over blindly avoiding underscore. Event -names should be ALL_CAPS with words separated by underscore. +names should be ALL_CAPS with words separated by underscore. Field +names cannot start with 'has-' or 'has_', as this is reserved for +tracking optional fields. Any name (command, event, type, field, or enum value) beginning with "x-" is marked experimental, and may be withdrawn or changed @@ -123,9 +125,10 @@ vendor), even if the rest of the name uses dash (example: __com.redhat_drive-mirror). Other than downstream extensions (with leading underscore and the use of dots), all names should begin with a letter, and contain only ASCII letters, digits, dash, and underscore. -It is okay to reuse names that match C keywords; the generator will -rename a field named "default" in the QAPI to "q_default" in the -generated C code. +Names beginning with 'q_' are reserved for the generator: QMP names +that resemble C keywords or other problematic strings will be munged +in C to use this prefix. For example, a field named "default" in +qapi becomes "q_default" in the generated C code. In the rest of this document, usage lines are given for each expression type, with literal strings written in lower case and diff --git a/scripts/qapi.py b/scripts/qapi.py index d53b5c4b45..3ff7b11e61 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -376,7 +376,9 @@ def check_name(expr_info, source, name, allow_optional=False, # code always prefixes it with the enum name if enum_member: membername = '_' + membername - if not valid_name.match(membername): + # Reserve the entire 'q_' namespace for c_name() + if not valid_name.match(membername) or \ + c_name(membername, False).startswith('q_'): raise QAPIExprError(expr_info, "%s uses invalid name '%s'" % (source, name)) @@ -488,6 +490,10 @@ def check_type(expr_info, source, value, allow_array=False, for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) + if c_name(key, False).startswith('has_'): + raise QAPIExprError(expr_info, + "Member of %s uses reserved name '%s'" + % (source, key)) # Todo: allow dictionaries to represent default values of # an optional argument. check_type(expr_info, "Member '%s' of %s" % (key, source), arg, diff --git a/tests/qapi-schema/reserved-command-q.err b/tests/qapi-schema/reserved-command-q.err index e69de29bb2..f939e044eb 100644 --- a/tests/qapi-schema/reserved-command-q.err +++ b/tests/qapi-schema/reserved-command-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-command-q.json:5: 'command' uses invalid name 'q-unix' diff --git a/tests/qapi-schema/reserved-command-q.exit b/tests/qapi-schema/reserved-command-q.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-command-q.exit +++ b/tests/qapi-schema/reserved-command-q.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-command-q.json b/tests/qapi-schema/reserved-command-q.json index be9944c68a..99f8aae314 100644 --- a/tests/qapi-schema/reserved-command-q.json +++ b/tests/qapi-schema/reserved-command-q.json @@ -1,7 +1,5 @@ # C entity name collision -# FIXME - This parses, but fails to compile, because it attempts to declare -# two 'qmp_q_unix' functions (one for 'q-unix', the other because c_name() -# munges 'unix' to 'q_unix' to avoid reserved word collisions). We should -# reject attempts to explicitly use 'q_' names, to reserve it for qapi. +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. { 'command': 'unix' } { 'command': 'q-unix' } diff --git a/tests/qapi-schema/reserved-command-q.out b/tests/qapi-schema/reserved-command-q.out index b31b38ff0d..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-command-q.out +++ b/tests/qapi-schema/reserved-command-q.out @@ -1,5 +0,0 @@ -object :empty -command q-unix None -> None - gen=True success_response=True -command unix None -> None - gen=True success_response=True diff --git a/tests/qapi-schema/reserved-member-has.err b/tests/qapi-schema/reserved-member-has.err index e69de29bb2..e755771446 100644 --- a/tests/qapi-schema/reserved-member-has.err +++ b/tests/qapi-schema/reserved-member-has.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-has.json:5: Member of 'data' for command 'oops' uses reserved name 'has-a' diff --git a/tests/qapi-schema/reserved-member-has.exit b/tests/qapi-schema/reserved-member-has.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-member-has.exit +++ b/tests/qapi-schema/reserved-member-has.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-member-has.json b/tests/qapi-schema/reserved-member-has.json index a2197de6b5..45b9109bdc 100644 --- a/tests/qapi-schema/reserved-member-has.json +++ b/tests/qapi-schema/reserved-member-has.json @@ -1,6 +1,5 @@ # C member name collision -# FIXME - This parses, but fails to compile, because the C struct is given -# two 'has_a' members, one from the flag for optional 'a', and the other -# from member 'has-a'. Either reject this at parse time, or munge the C -# names to avoid the collision. +# We reject names like 'has-a', because they can collide with the flag +# for an optional 'a' in generated C. +# TODO we could munge the optional flag name to avoid the collision. { 'command': 'oops', 'data': { '*a': 'str', 'has-a': 'str' } } diff --git a/tests/qapi-schema/reserved-member-has.out b/tests/qapi-schema/reserved-member-has.out index 5a18b6be8c..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-member-has.out +++ b/tests/qapi-schema/reserved-member-has.out @@ -1,6 +0,0 @@ -object :empty -object :obj-oops-arg - member a: str optional=True - member has-a: str optional=False -command oops :obj-oops-arg -> None - gen=True success_response=True diff --git a/tests/qapi-schema/reserved-member-q.err b/tests/qapi-schema/reserved-member-q.err index e69de29bb2..f3d5dd7818 100644 --- a/tests/qapi-schema/reserved-member-q.err +++ b/tests/qapi-schema/reserved-member-q.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-q.json:4: Member of 'data' for struct 'Foo' uses invalid name 'q-unix' diff --git a/tests/qapi-schema/reserved-member-q.exit b/tests/qapi-schema/reserved-member-q.exit index 573541ac97..d00491fd7e 100644 --- a/tests/qapi-schema/reserved-member-q.exit +++ b/tests/qapi-schema/reserved-member-q.exit @@ -1 +1 @@ -0 +1 diff --git a/tests/qapi-schema/reserved-member-q.json b/tests/qapi-schema/reserved-member-q.json index 1602ed3281..62fed8fddf 100644 --- a/tests/qapi-schema/reserved-member-q.json +++ b/tests/qapi-schema/reserved-member-q.json @@ -1,6 +1,4 @@ # C member name collision -# FIXME - This parses, but fails to compile, because it attempts to declare -# two 'q_unix' members (one for 'q-unix', the other because c_name() -# munges 'unix' to 'q_unix' to avoid reserved word collisions). We should -# reject attempts to explicitly use 'q_' names, to reserve it for qapi. +# We reject names like 'q-unix', because they can collide with the mangled +# name for 'unix' in generated C. { 'struct': 'Foo', 'data': { 'unix':'int', 'q-unix':'bool' } } diff --git a/tests/qapi-schema/reserved-member-q.out b/tests/qapi-schema/reserved-member-q.out index 0d8685aeb0..e69de29bb2 100644 --- a/tests/qapi-schema/reserved-member-q.out +++ b/tests/qapi-schema/reserved-member-q.out @@ -1,4 +0,0 @@ -object :empty -object Foo - member unix: int optional=False - member q-unix: bool optional=False From 98481bfcd661daa3c160cc87a297b0e60a307788 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:45 -0600 Subject: [PATCH 07/25] vnc: Hoist allocation of VncBasicInfo to callers A future qapi patch will rework generated structs with a base class to be unboxed. In preparation for that, change the code that allocates then populates an info struct to instead merely populate the fields of an info field passed in as a parameter (renaming vnc_basic_info_get* to vnc_init_basic_info*). Add rudimentary Error handling at the lowest levels for cases where the old code returned NULL; but rather than plumb Error all the way through the stack, the callers drop the error and return NULL as before. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-7-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- ui/vnc.c | 57 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index faff0546e8..502a10a07b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -156,10 +156,11 @@ char *vnc_socket_remote_addr(const char *format, int fd) { return addr_to_string(format, &sa, salen); } -static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa, - socklen_t salen) +static void vnc_init_basic_info(struct sockaddr_storage *sa, + socklen_t salen, + VncBasicInfo *info, + Error **errp) { - VncBasicInfo *info; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; int err; @@ -168,42 +169,44 @@ static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - VNC_DEBUG("Cannot resolve address %d: %s\n", - err, gai_strerror(err)); - return NULL; + error_setg(errp, "Cannot resolve address: %s", + gai_strerror(err)); + return; } - info = g_malloc0(sizeof(VncBasicInfo)); info->host = g_strdup(host); info->service = g_strdup(serv); info->family = inet_netfamily(sa->ss_family); - return info; } -static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd) +static void vnc_init_basic_info_from_server_addr(int fd, VncBasicInfo *info, + Error **errp) { struct sockaddr_storage sa; socklen_t salen; salen = sizeof(sa); if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) { - return NULL; + error_setg_errno(errp, errno, "getsockname failed"); + return; } - return vnc_basic_info_get(&sa, salen); + vnc_init_basic_info(&sa, salen, info, errp); } -static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd) +static void vnc_init_basic_info_from_remote_addr(int fd, VncBasicInfo *info, + Error **errp) { struct sockaddr_storage sa; socklen_t salen; salen = sizeof(sa); if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) { - return NULL; + error_setg_errno(errp, errno, "getpeername failed"); + return; } - return vnc_basic_info_get(&sa, salen); + vnc_init_basic_info(&sa, salen, info, errp); } static const char *vnc_auth_name(VncDisplay *vd) { @@ -256,15 +259,18 @@ static const char *vnc_auth_name(VncDisplay *vd) { static VncServerInfo *vnc_server_info_get(VncDisplay *vd) { VncServerInfo *info; - VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock); - if (!bi) { - return NULL; - } + Error *err = NULL; info = g_malloc(sizeof(*info)); - info->base = bi; + info->base = g_malloc(sizeof(*info->base)); + vnc_init_basic_info_from_server_addr(vd->lsock, info->base, &err); info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); + if (err) { + qapi_free_VncServerInfo(info); + info = NULL; + error_free(err); + } return info; } @@ -291,11 +297,16 @@ static void vnc_client_cache_auth(VncState *client) static void vnc_client_cache_addr(VncState *client) { - VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock); + Error *err = NULL; - if (bi) { - client->info = g_malloc0(sizeof(*client->info)); - client->info->base = bi; + 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, + &err); + if (err) { + qapi_free_VncClientInfo(client->info); + client->info = NULL; + error_free(err); } } From d02cf37766ba3cf918d7085aa7848c9dc05fd11a Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:46 -0600 Subject: [PATCH 08/25] qapi-visit: Split off visit_type_FOO_fields forward decl We generate a static visit_type_FOO_fields() for every type FOO. However, sometimes we need a forward declaration. Split the code to generate the forward declaration out of gen_visit_implicit_struct() into a new gen_visit_fields_decl(), and also prepare for a forward declaration to be emitted during gen_visit_struct(), so that a future patch can switch from using visit_type_FOO_implicit() to the simpler visit_type_FOO_fields() as part of unboxing the base class of a struct. No change to generated code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-8-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi-visit.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 2dc3aed220..d4408f25b1 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -15,7 +15,12 @@ from qapi import * import re +# visit_type_FOO_implicit() is emitted as needed; track if it has already +# been output. implicit_structs_seen = set() + +# visit_type_FOO_fields() is always emitted; track if a forward declaration +# or implementation has already been output. struct_fields_seen = set() @@ -29,19 +34,24 @@ void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, Error ** c_name=c_name(name), c_type=c_type) -def gen_visit_implicit_struct(typ): - if typ in implicit_structs_seen: - return '' - implicit_structs_seen.add(typ) - +def gen_visit_fields_decl(typ): ret = '' if typ.name not in struct_fields_seen: - # Need a forward declaration ret += mcgen(''' static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp); ''', c_type=typ.c_name()) + struct_fields_seen.add(typ.name) + return ret + + +def gen_visit_implicit_struct(typ): + if typ in implicit_structs_seen: + return '' + implicit_structs_seen.add(typ) + + ret = gen_visit_fields_decl(typ) ret += mcgen(''' From f87ab7f9bd956250c48b5c6e9b607b537fd21543 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:47 -0600 Subject: [PATCH 09/25] qapi-types: Refactor base fields output Move code from gen_union() into gen_struct_fields() in order for a later patch to share code when enumerating inherited fields for struct types. No change to generated code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-9-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi-types.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 4fe618ef3c..40e9f79b63 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -51,10 +51,21 @@ def gen_struct_field(name, typ, optional): return ret -def gen_struct_fields(members): +def gen_struct_fields(local_members, base=None): ret = '' - for memb in members: + if base: + ret += mcgen(''' + /* Members inherited from %(c_name)s: */ +''', + c_name=base.c_name()) + for memb in base.members: + ret += gen_struct_field(memb.name, memb.type, memb.optional) + ret += mcgen(''' + /* Own members: */ +''') + + for memb in local_members: ret += gen_struct_field(memb.name, memb.type, memb.optional) return ret @@ -126,14 +137,7 @@ struct %(c_name)s { ''', c_name=c_name(name)) if base: - ret += mcgen(''' - /* Members inherited from %(c_name)s: */ -''', - c_name=c_name(base.name)) - ret += gen_struct_fields(base.members) - ret += mcgen(''' - /* Own members: */ -''') + ret += gen_struct_fields([], base) else: ret += mcgen(''' %(c_type)s kind; From 30594fe1cd4355626e73b80645428105d0df3cf6 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:48 -0600 Subject: [PATCH 10/25] qapi: Prefer typesafe upcasts to qapi base classes A previous patch (commit 1e6c1616) made it possible to directly cast from a qapi flat union type to its base type. However, it requires the use of a C cast, which turns off compiler type-safety checks. Fortunately, no such casts exist, just yet. Regardless, add inline type-safe wrappers named qapi_FOO_base() for any union type FOO that has a base, which can be used for a safer upcast, and enhance the testsuite to cover the new functionality. A future patch will extend the upcast support to structs, where such conversions do exist already. Note that C makes const-correct upcasts annoying because it lacks overloads; these functions cast away const so that they can accept user pointers whether const or not, and the result in turn can be assigned to normal or const pointers. Alternatively, this could have been done with macros, but type-safe macros are hairy, and not worthwhile here. This patch just adds upcasts. None of our code needed to downcast from a base qapi class to a child. Also, in the case of grandchildren (such as BlockdevOptionsQcow2), the caller will need to call two functions to get to the inner base (although it wouldn't be too hard to generate a qapi_FOO_base_base() if desired). If a user changes qapi to alter the base class hierarchy, such as going from 'A -> C' to 'A -> B -> C', it will change the type of 'qapi_C_base()', and the compiler will point out the places that are affected by the new base. One alternative was proposed, but was deemed too ugly to use in practice: the generators could output redundant information using anonymous types: | struct Child { | union { | struct { | Type1 parent_member1; | Type2 parent_member2; | }; | Parent base; | }; | }; With that ugly proposal, for a given qapi type, obj->member and obj->base.member would refer to the same storage; allowing convenience in working with members without needing 'base.' allowing typesafe upcast without needing a C cast by accessing '&obj->base', and allowing downcasts from the parent back to the child possible through container_of(obj, Child, base). Signed-off-by: Eric Blake Message-Id: <1445898903-12082-10-git-send-email-eblake@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster --- scripts/qapi-types.py | 16 ++++++++++++++++ tests/test-qmp-input-visitor.c | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 40e9f79b63..f9fcf150a4 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -98,6 +98,19 @@ struct %(c_name)s { return ret +def gen_upcast(name, base): + # C makes const-correctness ugly. We have to cast away const to let + # this function work for both const and non-const obj. + return mcgen(''' + +static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj) +{ + return (%(base)s *)obj; +} +''', + c_name=c_name(name), base=base.c_name()) + + def gen_alternate_qtypes_decl(name): return mcgen(''' @@ -267,6 +280,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): 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) self._gen_type_cleanup(name) diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 8941963c8d..da21709714 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -347,6 +347,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data, Visitor *v; Error *err = NULL; UserDefFlatUnion *tmp; + UserDefUnionBase *base; v = visitor_input_test_init(data, "{ 'enum1': 'value1', " @@ -360,6 +361,10 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data, g_assert_cmpstr(tmp->string, ==, "str"); g_assert_cmpint(tmp->integer, ==, 41); g_assert_cmpint(tmp->value1->boolean, ==, true); + + base = qapi_UserDefFlatUnion_base(tmp); + g_assert(&base->enum1 == &tmp->enum1); + qapi_free_UserDefFlatUnion(tmp); } From ddf21908961073199f3d186204da4810f2ea150b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:49 -0600 Subject: [PATCH 11/25] qapi: Unbox base members 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 1e6c1616a "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 Message-Id: <1445898903-12082-11-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- hmp.c | 6 ++--- scripts/qapi-types.py | 12 ++++------ scripts/qapi-visit.py | 9 ++++---- tests/Makefile | 1 - tests/qapi-schema/struct-base-clash-base.err | 0 tests/qapi-schema/struct-base-clash-base.exit | 1 - tests/qapi-schema/struct-base-clash-base.json | 9 -------- tests/qapi-schema/struct-base-clash-base.out | 5 ---- tests/test-qmp-commands.c | 15 ++++-------- tests/test-qmp-event.c | 8 ++----- tests/test-qmp-input-visitor.c | 4 ++-- tests/test-qmp-output-visitor.c | 13 ++++------- tests/test-visitor-serialization.c | 14 +++++------ ui/spice-core.c | 23 +++++++++++-------- ui/vnc.c | 21 ++++++++--------- 15 files changed, 54 insertions(+), 87 deletions(-) delete mode 100644 tests/qapi-schema/struct-base-clash-base.err delete mode 100644 tests/qapi-schema/struct-base-clash-base.exit delete mode 100644 tests/qapi-schema/struct-base-clash-base.json delete mode 100644 tests/qapi-schema/struct-base-clash-base.out diff --git a/hmp.c b/hmp.c index 5048eeeb2d..88fd804a9e 100644 --- a/hmp.c +++ b/hmp.c @@ -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"); @@ -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); diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index f9fcf150a4..faf7022e2c 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -77,16 +77,13 @@ struct %(c_name)s { ''', 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; ''') @@ -280,11 +277,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): 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): diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index d4408f25b1..f711a720f3 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -72,13 +72,12 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error * 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) @@ -90,9 +89,9 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e 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)->') diff --git a/tests/Makefile b/tests/Makefile index 652294cccd..fd4ec03e09 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 diff --git a/tests/qapi-schema/struct-base-clash-base.err b/tests/qapi-schema/struct-base-clash-base.err deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/struct-base-clash-base.exit b/tests/qapi-schema/struct-base-clash-base.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/struct-base-clash-base.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/struct-base-clash-base.json b/tests/qapi-schema/struct-base-clash-base.json deleted file mode 100644 index 0c840258c9..0000000000 --- a/tests/qapi-schema/struct-base-clash-base.json +++ /dev/null @@ -1,9 +0,0 @@ -# Struct member 'base' -# FIXME: this parses, but then fails to compile due to a duplicate 'base' -# (one explicit in QMP, the other used to box the base class members). -# We should either reject the collision at parse time, or change the -# generated struct to allow this to compile. -{ 'struct': 'Base', 'data': {} } -{ 'struct': 'Sub', - 'base': 'Base', - 'data': { 'base': 'str' } } diff --git a/tests/qapi-schema/struct-base-clash-base.out b/tests/qapi-schema/struct-base-clash-base.out deleted file mode 100644 index e69a416560..0000000000 --- a/tests/qapi-schema/struct-base-clash-base.out +++ /dev/null @@ -1,5 +0,0 @@ -object :empty -object Base -object Sub - base Base - member base: str optional=False diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index bc59835835..ea700d890d 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -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"); @@ -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)); diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 28f146d4b7..035c65cfdf 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -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; @@ -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; diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index da21709714..2d95db94e6 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -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); @@ -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); diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index c84002e2f2..cfb06bbbc6 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -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); @@ -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; @@ -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; diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index fa86cae88a..634563bae4 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -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; @@ -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, ==, diff --git a/ui/spice-core.c b/ui/spice-core.c index bf4fd07499..6a62d712fe 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -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 @@ -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", @@ -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) { @@ -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; @@ -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; diff --git a/ui/vnc.c b/ui/vnc.c index 502a10a07b..cec2cee993 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -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) { @@ -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); @@ -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) { @@ -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); @@ -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); From 5c5e51a05b567fd48fb155d94ca6f7679dd0d478 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:50 -0600 Subject: [PATCH 12/25] qapi-visit: Remove redundant functions for flat union base The code for visiting the base class of a child struct created visit_type_Base_fields() which covers all fields of Base; while the code for visiting the base class of a flat union created visit_type_Union_fields() covering all fields of the base except the discriminator. But since the base class includes the discriminator of a flat union, we can just visit the entire base, without needing a separate visit of the discriminator. Not only is consistently visiting all fields easier to understand, it lets us share code. The generated code in qapi-visit.c loses several now-unused visit_type_UNION_fields(), along with changes like: |@@ -1654,11 +1557,7 @@ void visit_type_BlockdevOptions(Visitor | if (!*obj) { | goto out_obj; | } |- visit_type_BlockdevOptions_fields(v, obj, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_BlockdevDriver(v, &(*obj)->driver, "driver", &err); |+ visit_type_BlockdevOptionsBase_fields(v, (BlockdevOptionsBase **)obj, &err); | if (err) { | goto out_obj; | } and forward declarations where needed. Note that the cast of obj to BASE ** is necessary to call visit_type_BASE_fields() (and we can't use our upcast wrappers, because those work on pointers while we have a pointer-to-pointer). Signed-off-by: Eric Blake Message-Id: <1445898903-12082-12-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi-visit.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index f711a720f3..33c013a7fb 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -228,8 +228,7 @@ def gen_visit_union(name, base, variants): ret = '' if base: - members = [m for m in base.members if m != variants.tag_member] - ret += gen_visit_struct_fields(name, None, members) + ret += gen_visit_fields_decl(base) for var in variants.variants: # Ugly special case for simple union TODO get rid of it @@ -254,31 +253,30 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error if base: ret += mcgen(''' - visit_type_%(c_name)s_fields(v, obj, &err); + visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err); ''', - c_name=c_name(name)) - ret += gen_err_check(label='out_obj') - - tag_key = variants.tag_member.name - if not variants.tag_name: - # we pointlessly use a different key for simple unions - tag_key = 'type' - ret += mcgen(''' + c_name=base.c_name()) + else: + ret += mcgen(''' visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err); - if (err) { - goto out_obj; - } +''', + c_type=variants.tag_member.type.c_name(), + # TODO ugly special case for simple union + # Use same tag name in C as on the wire to get rid of + # it, then: c_name=c_name(variants.tag_member.name) + c_name='kind', + name=variants.tag_member.name) + ret += gen_err_check(label='out_obj') + ret += mcgen(''' if (!visit_start_union(v, !!(*obj)->data, &err) || err) { goto out_obj; } switch ((*obj)->%(c_name)s) { ''', - c_type=variants.tag_member.type.c_name(), # TODO ugly special case for simple union # Use same tag name in C as on the wire to get rid of # it, then: c_name=c_name(variants.tag_member.name) - c_name=c_name(variants.tag_name or 'kind'), - name=tag_key) + c_name=c_name(variants.tag_name or 'kind')) for var in variants.variants: # TODO ugly special case for simple union From f51d8fab44b231aa299d8de24cfdf9ba41ef4a21 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:51 -0600 Subject: [PATCH 13/25] qapi: Start converting to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. This patch is the front end for a series that converts to a saner qapi union layout. By the end of the series, we will no longer have the type/kind mismatch, and all tag values will be under a named union, which requires clients to access 'obj->u.value' instead of 'obj->value'. But since the conversion touches a number of files, it is easiest if we temporarily support BOTH layouts simultaneously. Given a simple union qapi type: { 'union':'Foo', 'data': { 'a':'int', 'b':'bool' } } make the following changes in generated qapi-types.h: | struct Foo { |- FooKind kind; |- union { /* union tag is @kind */ |+ union { |+ FooKind kind; |+ FooKind type; |+ }; |+ union { /* union tag is @type */ | void *data; | int64_t a; | bool b; |+ union { /* union tag is @type */ |+ void *data; |+ int64_t a; |+ bool b; |+ } u; | }; | }; Flat unions do not need the anonymous union for the tag member, as we already fixed that to use the member name instead of 'kind' back in commit 0f61af3e. One additional change is needed in qapi.py: check_union() now needs to check for collisions with 'type' in addition to those with 'kind'. Later, when the conversions are complete, we will remove the duplication hacks, and also drop the check_union() restrictions. Note, however, that we do not rename the generated enum, which is still 'FooKind'. A further patch could generate implicit enums as 'FooType', but while the generator already reserved the '*Kind' namespace (commit 4dc2e69), there are already QMP constructs with '*Type' naming, which means changing our reservation namespace would have lots of churn to C code to deal with a forced name change. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-13-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- scripts/qapi-types.py | 26 +++++++++++++++++++------- scripts/qapi.py | 3 ++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index faf7022e2c..1420e00ffb 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -149,11 +149,23 @@ struct %(c_name)s { if base: ret += gen_struct_fields([], base) else: + # TODO As a hack, we emit both 'kind' and 'type'. Ultimately, we + # want to use only 'type', but the conversion is large enough to + # require staging over several commits. ret += mcgen(''' - %(c_type)s kind; + union { + %(c_type)s kind; + %(c_type)s type; + }; ''', c_type=c_name(variants.tag_member.type.name)) + # TODO As a hack, we emit the union twice, once as an anonymous union + # and once as a named union. Ultimately, we want to use only the + # named union version (as it avoids conflicts between tag values as + # branch names competing with non-variant QMP names), but the conversion + # is large enough to require staging over several commits. + tmp = '' # 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 @@ -162,25 +174,25 @@ struct %(c_name)s { # should not be any data leaks even without a data pointer. Or, if # 'data' is merely added to guarantee we don't have an empty union, # shouldn't we enforce that at .json parse time? - ret += mcgen(''' + tmp += mcgen(''' union { /* union tag is @%(c_name)s */ void *data; ''', - # TODO ugly special case for simple union - # Use same tag name in C as on the wire to get rid of - # it, then: c_name=c_name(variants.tag_member.name) - c_name=c_name(variants.tag_name or 'kind')) + c_name=c_name(variants.tag_member.name)) for var in variants.variants: # Ugly special case for simple union TODO get rid of it typ = var.simple_union_type() or var.type - ret += mcgen(''' + tmp += mcgen(''' %(c_type)s %(c_name)s; ''', c_type=typ.c_type(), c_name=c_name(var.name)) + ret += tmp + ret += ' ' + '\n '.join(tmp.split('\n')) ret += mcgen(''' + } u; }; }; ''') diff --git a/scripts/qapi.py b/scripts/qapi.py index 3ff7b11e61..00a16203df 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -548,7 +548,8 @@ def check_union(expr, expr_info): base = expr.get('base') discriminator = expr.get('discriminator') members = expr['data'] - values = {'MAX': '(automatic)', 'KIND': '(automatic)'} + values = {'MAX': '(automatic)', 'KIND': '(automatic)', + 'TYPE': '(automatic)'} # Two types of unions, determined by discriminator. From 150d0564a4c626642897c748f7906260a13c14e1 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:52 -0600 Subject: [PATCH 14/25] qapi-visit: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for qapi-visit.py. Generated code changes look like: |@@ -4912,16 +4912,16 @@ void visit_type_MemoryDeviceInfo(Visitor | if (!*obj) { | goto out_obj; | } |- visit_type_MemoryDeviceInfoKind(v, &(*obj)->kind, "type", &err); |+ visit_type_MemoryDeviceInfoKind(v, &(*obj)->type, "type", &err); | if (err) { | goto out_obj; | } |- if (!visit_start_union(v, !!(*obj)->data, &err) || err) { |+ if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) { | goto out_obj; | } |- switch ((*obj)->kind) { |+ switch ((*obj)->type) { | case MEMORY_DEVICE_INFO_KIND_DIMM: |- visit_type_PCDIMMDeviceInfo(v, &(*obj)->dimm, "data", &err); |+ visit_type_PCDIMMDeviceInfo(v, &(*obj)->u.dimm, "data", &err); | break; | default: | abort(); |@@ -4930,7 +4930,7 @@ out_obj: | error_propagate(errp, err); | err = NULL; | if (*obj) { |- visit_end_union(v, !!(*obj)->data, &err); |+ visit_end_union(v, !!(*obj)->u.data, &err); | } | error_propagate(errp, err); | err = NULL; Signed-off-by: Eric Blake Message-Id: <1445898903-12082-14-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- scripts/qapi-visit.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 33c013a7fb..f40c3c792f 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -189,18 +189,18 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error if (err) { goto out; } - visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err); + visit_get_next_type(v, (int*) &(*obj)->type, %(c_name)s_qtypes, name, &err); if (err) { goto out_obj; } - switch ((*obj)->kind) { + switch ((*obj)->type) { ''', c_name=c_name(name)) for var in variants.variants: ret += mcgen(''' case %(case)s: - visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err); + visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, name, &err); break; ''', case=c_enum_const(variants.tag_member.type.name, @@ -261,22 +261,16 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err); ''', c_type=variants.tag_member.type.c_name(), - # TODO ugly special case for simple union - # Use same tag name in C as on the wire to get rid of - # it, then: c_name=c_name(variants.tag_member.name) - c_name='kind', + c_name=c_name(variants.tag_member.name), name=variants.tag_member.name) ret += gen_err_check(label='out_obj') ret += mcgen(''' - if (!visit_start_union(v, !!(*obj)->data, &err) || err) { + if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) { goto out_obj; } switch ((*obj)->%(c_name)s) { ''', - # TODO ugly special case for simple union - # Use same tag name in C as on the wire to get rid of - # it, then: c_name=c_name(variants.tag_member.name) - c_name=c_name(variants.tag_name or 'kind')) + c_name=c_name(variants.tag_member.name)) for var in variants.variants: # TODO ugly special case for simple union @@ -288,13 +282,13 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error var.name)) if simple_union_type: ret += mcgen(''' - visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err); + visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, "data", &err); ''', c_type=simple_union_type.c_name(), c_name=c_name(var.name)) else: ret += mcgen(''' - visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err); + visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err); ''', c_type=var.type.c_name(), c_name=c_name(var.name)) @@ -310,7 +304,7 @@ out_obj: error_propagate(errp, err); err = NULL; if (*obj) { - visit_end_union(v, !!(*obj)->data, &err); + visit_end_union(v, !!(*obj)->u.data, &err); } error_propagate(errp, err); err = NULL; From c363acef772647f66becdbf46dd54e70e67f3cc9 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:53 -0600 Subject: [PATCH 15/25] tests: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for testsuite code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-15-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- tests/test-qmp-commands.c | 4 +- tests/test-qmp-input-visitor.c | 78 ++++++++++++++++----------------- tests/test-qmp-output-visitor.c | 42 +++++++++--------- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index ea700d890d..f23d8eaf2a 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -62,8 +62,8 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, { __org_qemu_x_Union1 *ret = g_new0(__org_qemu_x_Union1, 1); - ret->kind = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; - ret->__org_qemu_x_branch = strdup("blah1"); + ret->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + ret->u.__org_qemu_x_branch = strdup("blah1"); return ret; } diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 2d95db94e6..de65982d47 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -360,7 +360,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data, g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1); g_assert_cmpstr(tmp->string, ==, "str"); g_assert_cmpint(tmp->integer, ==, 41); - g_assert_cmpint(tmp->value1->boolean, ==, true); + g_assert_cmpint(tmp->u.value1->boolean, ==, true); base = qapi_UserDefFlatUnion_base(tmp); g_assert(&base->enum1 == &tmp->enum1); @@ -377,15 +377,15 @@ static void test_visitor_in_alternate(TestInputVisitorData *data, v = visitor_input_test_init(data, "42"); visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort); - g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_I); - g_assert_cmpint(tmp->i, ==, 42); + g_assert_cmpint(tmp->type, ==, USER_DEF_ALTERNATE_KIND_I); + g_assert_cmpint(tmp->u.i, ==, 42); qapi_free_UserDefAlternate(tmp); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, "'string'"); visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort); - g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_S); - g_assert_cmpstr(tmp->s, ==, "string"); + g_assert_cmpint(tmp->type, ==, USER_DEF_ALTERNATE_KIND_S); + g_assert_cmpstr(tmp->u.s, ==, "string"); qapi_free_UserDefAlternate(tmp); visitor_input_teardown(data, NULL); @@ -424,8 +424,8 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, * parse the same as ans */ v = visitor_input_test_init(data, "42"); visit_type_AltStrNum(v, &asn, NULL, &err); - /* FIXME g_assert_cmpint(asn->kind, == ALT_STR_NUM_KIND_N); */ - /* FIXME g_assert_cmpfloat(asn->n, ==, 42); */ + /* FIXME g_assert_cmpint(asn->type, == ALT_STR_NUM_KIND_N); */ + /* FIXME g_assert_cmpfloat(asn->u.n, ==, 42); */ g_assert(err); error_free(err); err = NULL; @@ -434,29 +434,29 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, v = visitor_input_test_init(data, "42"); visit_type_AltNumStr(v, &ans, NULL, &error_abort); - g_assert_cmpint(ans->kind, ==, ALT_NUM_STR_KIND_N); - g_assert_cmpfloat(ans->n, ==, 42); + g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N); + g_assert_cmpfloat(ans->u.n, ==, 42); qapi_free_AltNumStr(ans); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, "42"); visit_type_AltStrInt(v, &asi, NULL, &error_abort); - g_assert_cmpint(asi->kind, ==, ALT_STR_INT_KIND_I); - g_assert_cmpint(asi->i, ==, 42); + g_assert_cmpint(asi->type, ==, ALT_STR_INT_KIND_I); + g_assert_cmpint(asi->u.i, ==, 42); qapi_free_AltStrInt(asi); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, "42"); visit_type_AltIntNum(v, &ain, NULL, &error_abort); - g_assert_cmpint(ain->kind, ==, ALT_INT_NUM_KIND_I); - g_assert_cmpint(ain->i, ==, 42); + g_assert_cmpint(ain->type, ==, ALT_INT_NUM_KIND_I); + g_assert_cmpint(ain->u.i, ==, 42); qapi_free_AltIntNum(ain); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, "42"); visit_type_AltNumInt(v, &ani, NULL, &error_abort); - g_assert_cmpint(ani->kind, ==, ALT_NUM_INT_KIND_I); - g_assert_cmpint(ani->i, ==, 42); + g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_I); + g_assert_cmpint(ani->u.i, ==, 42); qapi_free_AltNumInt(ani); visitor_input_teardown(data, NULL); @@ -472,15 +472,15 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, v = visitor_input_test_init(data, "42.5"); visit_type_AltStrNum(v, &asn, NULL, &error_abort); - g_assert_cmpint(asn->kind, ==, ALT_STR_NUM_KIND_N); - g_assert_cmpfloat(asn->n, ==, 42.5); + g_assert_cmpint(asn->type, ==, ALT_STR_NUM_KIND_N); + g_assert_cmpfloat(asn->u.n, ==, 42.5); qapi_free_AltStrNum(asn); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, "42.5"); visit_type_AltNumStr(v, &ans, NULL, &error_abort); - g_assert_cmpint(ans->kind, ==, ALT_NUM_STR_KIND_N); - g_assert_cmpfloat(ans->n, ==, 42.5); + g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N); + g_assert_cmpfloat(ans->u.n, ==, 42.5); qapi_free_AltNumStr(ans); visitor_input_teardown(data, NULL); @@ -494,15 +494,15 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, v = visitor_input_test_init(data, "42.5"); visit_type_AltIntNum(v, &ain, NULL, &error_abort); - g_assert_cmpint(ain->kind, ==, ALT_INT_NUM_KIND_N); - g_assert_cmpfloat(ain->n, ==, 42.5); + g_assert_cmpint(ain->type, ==, ALT_INT_NUM_KIND_N); + g_assert_cmpfloat(ain->u.n, ==, 42.5); qapi_free_AltIntNum(ain); visitor_input_teardown(data, NULL); v = visitor_input_test_init(data, "42.5"); visit_type_AltNumInt(v, &ani, NULL, &error_abort); - g_assert_cmpint(ani->kind, ==, ALT_NUM_INT_KIND_N); - g_assert_cmpfloat(ani->n, ==, 42.5); + g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_N); + g_assert_cmpfloat(ani->u.n, ==, 42.5); qapi_free_AltNumInt(ani); visitor_input_teardown(data, NULL); } @@ -532,68 +532,68 @@ static void test_native_list_integer_helper(TestInputVisitorData *data, visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); g_assert(err == NULL); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, kind); + g_assert_cmpint(cvalue->type, ==, kind); switch (kind) { case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: { intList *elem = NULL; - for (i = 0, elem = cvalue->integer; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.integer; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S8: { int8List *elem = NULL; - for (i = 0, elem = cvalue->s8; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s8; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S16: { int16List *elem = NULL; - for (i = 0, elem = cvalue->s16; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s16; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S32: { int32List *elem = NULL; - for (i = 0, elem = cvalue->s32; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s32; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S64: { int64List *elem = NULL; - for (i = 0, elem = cvalue->s64; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.s64; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U8: { uint8List *elem = NULL; - for (i = 0, elem = cvalue->u8; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u8; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U16: { uint16List *elem = NULL; - for (i = 0, elem = cvalue->u16; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u16; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U32: { uint32List *elem = NULL; - for (i = 0, elem = cvalue->u32; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u32; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U64: { uint64List *elem = NULL; - for (i = 0, elem = cvalue->u64; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.u64; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); } break; @@ -695,9 +695,9 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data, visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); g_assert(err == NULL); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN); + g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN); - for (i = 0, elem = cvalue->boolean; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.boolean; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, (i % 3 == 0) ? 1 : 0); } @@ -730,9 +730,9 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data, visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); g_assert(err == NULL); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING); + g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING); - for (i = 0, elem = cvalue->string; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.string; elem; elem = elem->next, i++) { gchar str[8]; sprintf(str, "%d", i); g_assert_cmpstr(elem->value, ==, str); @@ -769,9 +769,9 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data, visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err); g_assert(err == NULL); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->kind, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER); + g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER); - for (i = 0, elem = cvalue->number; elem; elem = elem->next, i++) { + for (i = 0, elem = cvalue->u.number; elem; elem = elem->next, i++) { GString *double_expected = g_string_new(""); GString *double_actual = g_string_new(""); diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index cfb06bbbc6..09d0dd81f0 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -487,9 +487,9 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); tmp->enum1 = ENUM_ONE_VALUE1; tmp->string = g_strdup("str"); - tmp->value1 = g_malloc0(sizeof(UserDefA)); - /* TODO when generator bug is fixed: tmp->integer = 41; */ - tmp->value1->boolean = true; + tmp->u.value1 = g_malloc0(sizeof(UserDefA)); + tmp->integer = 41; + tmp->u.value1->boolean = true; visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &err); g_assert(err == NULL); @@ -500,7 +500,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); - /* TODO g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); */ + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); qapi_free_UserDefFlatUnion(tmp); @@ -514,8 +514,8 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, Error *err = NULL; UserDefAlternate *tmp = g_malloc0(sizeof(UserDefAlternate)); - tmp->kind = USER_DEF_ALTERNATE_KIND_I; - tmp->i = 42; + tmp->type = USER_DEF_ALTERNATE_KIND_I; + tmp->u.i = 42; visit_type_UserDefAlternate(data->ov, &tmp, NULL, &err); g_assert(err == NULL); @@ -540,9 +540,9 @@ static void test_visitor_out_empty(TestOutputVisitorData *data, static void init_native_list(UserDefNativeListUnion *cvalue) { int i; - switch (cvalue->kind) { + switch (cvalue->type) { case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: { - intList **list = &cvalue->integer; + intList **list = &cvalue->u.integer; for (i = 0; i < 32; i++) { *list = g_new0(intList, 1); (*list)->value = i; @@ -552,7 +552,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S8: { - int8List **list = &cvalue->s8; + int8List **list = &cvalue->u.s8; for (i = 0; i < 32; i++) { *list = g_new0(int8List, 1); (*list)->value = i; @@ -562,7 +562,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S16: { - int16List **list = &cvalue->s16; + int16List **list = &cvalue->u.s16; for (i = 0; i < 32; i++) { *list = g_new0(int16List, 1); (*list)->value = i; @@ -572,7 +572,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S32: { - int32List **list = &cvalue->s32; + int32List **list = &cvalue->u.s32; for (i = 0; i < 32; i++) { *list = g_new0(int32List, 1); (*list)->value = i; @@ -582,7 +582,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_S64: { - int64List **list = &cvalue->s64; + int64List **list = &cvalue->u.s64; for (i = 0; i < 32; i++) { *list = g_new0(int64List, 1); (*list)->value = i; @@ -592,7 +592,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U8: { - uint8List **list = &cvalue->u8; + uint8List **list = &cvalue->u.u8; for (i = 0; i < 32; i++) { *list = g_new0(uint8List, 1); (*list)->value = i; @@ -602,7 +602,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U16: { - uint16List **list = &cvalue->u16; + uint16List **list = &cvalue->u.u16; for (i = 0; i < 32; i++) { *list = g_new0(uint16List, 1); (*list)->value = i; @@ -612,7 +612,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U32: { - uint32List **list = &cvalue->u32; + uint32List **list = &cvalue->u.u32; for (i = 0; i < 32; i++) { *list = g_new0(uint32List, 1); (*list)->value = i; @@ -622,7 +622,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_U64: { - uint64List **list = &cvalue->u64; + uint64List **list = &cvalue->u.u64; for (i = 0; i < 32; i++) { *list = g_new0(uint64List, 1); (*list)->value = i; @@ -632,7 +632,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: { - boolList **list = &cvalue->boolean; + boolList **list = &cvalue->u.boolean; for (i = 0; i < 32; i++) { *list = g_new0(boolList, 1); (*list)->value = (i % 3 == 0); @@ -642,7 +642,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: { - strList **list = &cvalue->string; + strList **list = &cvalue->u.string; for (i = 0; i < 32; i++) { *list = g_new0(strList, 1); (*list)->value = g_strdup_printf("%d", i); @@ -652,7 +652,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) break; } case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: { - numberList **list = &cvalue->number; + numberList **list = &cvalue->u.number; for (i = 0; i < 32; i++) { *list = g_new0(numberList, 1); (*list)->value = (double)i / 3; @@ -761,14 +761,14 @@ static void test_native_list(TestOutputVisitorData *data, Error *err = NULL; QObject *obj; - cvalue->kind = kind; + cvalue->type = kind; init_native_list(cvalue); visit_type_UserDefNativeListUnion(data->ov, &cvalue, NULL, &err); g_assert(err == NULL); obj = qmp_output_get_qobject(data->qov); - check_native_list(obj, cvalue->kind); + check_native_list(obj, cvalue->type); qapi_free_UserDefNativeListUnion(cvalue); qobject_decref(obj); } From 6a8f9661dc3c088ed0d2f5b41d940190407cbdc5 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:54 -0600 Subject: [PATCH 16/25] block: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for block-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-16-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- block/nbd.c | 18 +++++++++--------- block/qcow2.c | 10 ++++------ block/vmdk.c | 6 +++--- blockdev.c | 47 ++++++++++++++++++++++++----------------------- 4 files changed, 40 insertions(+), 41 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index c2a87e99bb..cd6a587776 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -206,24 +206,24 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export, saddr = g_new0(SocketAddress, 1); if (qdict_haskey(options, "path")) { - saddr->kind = SOCKET_ADDRESS_KIND_UNIX; - saddr->q_unix = g_new0(UnixSocketAddress, 1); - saddr->q_unix->path = g_strdup(qdict_get_str(options, "path")); + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + saddr->u.q_unix = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path")); qdict_del(options, "path"); } else { - saddr->kind = SOCKET_ADDRESS_KIND_INET; - saddr->inet = g_new0(InetSocketAddress, 1); - saddr->inet->host = g_strdup(qdict_get_str(options, "host")); + saddr->type = SOCKET_ADDRESS_KIND_INET; + saddr->u.inet = g_new0(InetSocketAddress, 1); + saddr->u.inet->host = g_strdup(qdict_get_str(options, "host")); if (!qdict_get_try_str(options, "port")) { - saddr->inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); + saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); } else { - saddr->inet->port = g_strdup(qdict_get_str(options, "port")); + saddr->u.inet->port = g_strdup(qdict_get_str(options, "port")); } qdict_del(options, "host"); qdict_del(options, "port"); } - s->client.is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX; + s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; *export = g_strdup(qdict_get_try_str(options, "export")); if (*export) { diff --git a/block/qcow2.c b/block/qcow2.c index bacc4f2e11..88f56c8868 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2738,18 +2738,16 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); *spec_info = (ImageInfoSpecific){ - .kind = IMAGE_INFO_SPECIFIC_KIND_QCOW2, - { - .qcow2 = g_new(ImageInfoSpecificQCow2, 1), - }, + .type = IMAGE_INFO_SPECIFIC_KIND_QCOW2, + .u.qcow2 = g_new(ImageInfoSpecificQCow2, 1), }; if (s->qcow_version == 2) { - *spec_info->qcow2 = (ImageInfoSpecificQCow2){ + *spec_info->u.qcow2 = (ImageInfoSpecificQCow2){ .compat = g_strdup("0.10"), .refcount_bits = s->refcount_bits, }; } else if (s->qcow_version == 3) { - *spec_info->qcow2 = (ImageInfoSpecificQCow2){ + *spec_info->u.qcow2 = (ImageInfoSpecificQCow2){ .compat = g_strdup("1.1"), .lazy_refcounts = s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS, diff --git a/block/vmdk.c b/block/vmdk.c index 0effb7d91c..6f819e413f 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2161,19 +2161,19 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) ImageInfoList **next; *spec_info = (ImageInfoSpecific){ - .kind = IMAGE_INFO_SPECIFIC_KIND_VMDK, + .type = IMAGE_INFO_SPECIFIC_KIND_VMDK, { .vmdk = g_new0(ImageInfoSpecificVmdk, 1), }, }; - *spec_info->vmdk = (ImageInfoSpecificVmdk) { + *spec_info->u.vmdk = (ImageInfoSpecificVmdk) { .create_type = g_strdup(s->create_type), .cid = s->cid, .parent_cid = s->parent_cid, }; - next = &spec_info->vmdk->extents; + next = &spec_info->u.vmdk->extents; for (i = 0; i < s->num_extents; i++) { *next = g_new0(ImageInfoList, 1); (*next)->value = vmdk_get_extent_info(&s->extents[i]); diff --git a/blockdev.c b/blockdev.c index 18712d25cc..8b8bfa992c 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1137,13 +1137,14 @@ void hmp_commit(Monitor *mon, const QDict *qdict) } } -static void blockdev_do_action(int kind, void *data, Error **errp) +static void blockdev_do_action(TransactionActionKind type, void *data, + Error **errp) { TransactionAction action; TransactionActionList list; - action.kind = kind; - action.data = data; + action.type = type; + action.u.data = data; list.value = &action; list.next = NULL; qmp_transaction(&list, errp); @@ -1388,9 +1389,9 @@ static void internal_snapshot_prepare(BlkTransactionState *common, InternalSnapshotState *state; int ret1; - g_assert(common->action->kind == + g_assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC); - internal = common->action->blockdev_snapshot_internal_sync; + internal = common->action->u.blockdev_snapshot_internal_sync; state = DO_UPCAST(InternalSnapshotState, common, common); /* 1. parse input */ @@ -1536,22 +1537,22 @@ static void external_snapshot_prepare(BlkTransactionState *common, TransactionAction *action = common->action; /* get parameters */ - g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC); + g_assert(action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC); - has_device = action->blockdev_snapshot_sync->has_device; - device = action->blockdev_snapshot_sync->device; - has_node_name = action->blockdev_snapshot_sync->has_node_name; - node_name = action->blockdev_snapshot_sync->node_name; + has_device = action->u.blockdev_snapshot_sync->has_device; + device = action->u.blockdev_snapshot_sync->device; + has_node_name = action->u.blockdev_snapshot_sync->has_node_name; + node_name = action->u.blockdev_snapshot_sync->node_name; has_snapshot_node_name = - action->blockdev_snapshot_sync->has_snapshot_node_name; - snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name; + action->u.blockdev_snapshot_sync->has_snapshot_node_name; + snapshot_node_name = action->u.blockdev_snapshot_sync->snapshot_node_name; - new_image_file = action->blockdev_snapshot_sync->snapshot_file; - if (action->blockdev_snapshot_sync->has_format) { - format = action->blockdev_snapshot_sync->format; + new_image_file = action->u.blockdev_snapshot_sync->snapshot_file; + if (action->u.blockdev_snapshot_sync->has_format) { + format = action->u.blockdev_snapshot_sync->format; } - if (action->blockdev_snapshot_sync->has_mode) { - mode = action->blockdev_snapshot_sync->mode; + if (action->u.blockdev_snapshot_sync->has_mode) { + mode = action->u.blockdev_snapshot_sync->mode; } /* start processing */ @@ -1681,8 +1682,8 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) DriveBackup *backup; Error *local_err = NULL; - assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); - backup = common->action->drive_backup; + assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); + backup = common->action->u.drive_backup; blk = blk_by_name(backup->device); if (!blk) { @@ -1754,8 +1755,8 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) BlockBackend *blk, *target; Error *local_err = NULL; - assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); - backup = common->action->blockdev_backup; + assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); + backup = common->action->u.blockdev_backup; blk = blk_by_name(backup->device); if (!blk) { @@ -1887,9 +1888,9 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) dev_info = dev_entry->value; dev_entry = dev_entry->next; - assert(dev_info->kind < ARRAY_SIZE(actions)); + assert(dev_info->type < ARRAY_SIZE(actions)); - ops = &actions[dev_info->kind]; + ops = &actions[dev_info->type]; assert(ops->instance_size > 0); state = g_malloc0(ops->instance_size); From 2d32addae70987521578d8bb27c6b3f52cdcbdcb Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:55 -0600 Subject: [PATCH 17/25] sockets: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for socket-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-17-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- qemu-nbd.c | 16 ++++++------ ui/vnc.c | 44 ++++++++++++++++---------------- util/qemu-sockets.c | 62 ++++++++++++++++++++++----------------------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/qemu-nbd.c b/qemu-nbd.c index 422a607bdd..3afec76504 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -362,17 +362,17 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath, saddr = g_new0(SocketAddress, 1); if (sockpath) { - saddr->kind = SOCKET_ADDRESS_KIND_UNIX; - saddr->q_unix = g_new0(UnixSocketAddress, 1); - saddr->q_unix->path = g_strdup(sockpath); + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + saddr->u.q_unix = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix->path = g_strdup(sockpath); } else { - saddr->kind = SOCKET_ADDRESS_KIND_INET; - saddr->inet = g_new0(InetSocketAddress, 1); - saddr->inet->host = g_strdup(bindto); + saddr->type = SOCKET_ADDRESS_KIND_INET; + saddr->u.inet = g_new0(InetSocketAddress, 1); + saddr->u.inet->host = g_strdup(bindto); if (port) { - saddr->inet->port = g_strdup(port); + saddr->u.inet->port = g_strdup(port); } else { - saddr->inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); + saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); } } diff --git a/ui/vnc.c b/ui/vnc.c index cec2cee993..7b37e3b01f 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3524,9 +3524,9 @@ void vnc_display_open(const char *id, Error **errp) } if (strncmp(vnc, "unix:", 5) == 0) { - saddr->kind = SOCKET_ADDRESS_KIND_UNIX; - saddr->q_unix = g_new0(UnixSocketAddress, 1); - saddr->q_unix->path = g_strdup(vnc + 5); + saddr->type = SOCKET_ADDRESS_KIND_UNIX; + saddr->u.q_unix = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix->path = g_strdup(vnc + 5); if (vs->ws_enabled) { error_setg(errp, "UNIX sockets not supported with websock"); @@ -3534,12 +3534,12 @@ void vnc_display_open(const char *id, Error **errp) } } else { unsigned long long baseport; - saddr->kind = SOCKET_ADDRESS_KIND_INET; - saddr->inet = g_new0(InetSocketAddress, 1); + saddr->type = SOCKET_ADDRESS_KIND_INET; + saddr->u.inet = g_new0(InetSocketAddress, 1); if (vnc[0] == '[' && vnc[hlen - 1] == ']') { - saddr->inet->host = g_strndup(vnc + 1, hlen - 2); + saddr->u.inet->host = g_strndup(vnc + 1, hlen - 2); } else { - saddr->inet->host = g_strndup(vnc, hlen); + saddr->u.inet->host = g_strndup(vnc, hlen); } if (parse_uint_full(h + 1, &baseport, 10) < 0) { error_setg(errp, "can't convert to a number: %s", h + 1); @@ -3550,28 +3550,28 @@ void vnc_display_open(const char *id, Error **errp) error_setg(errp, "port %s out of range", h + 1); goto fail; } - saddr->inet->port = g_strdup_printf( + saddr->u.inet->port = g_strdup_printf( "%d", (int)baseport + 5900); if (to) { - saddr->inet->has_to = true; - saddr->inet->to = to; + saddr->u.inet->has_to = true; + saddr->u.inet->to = to; } - saddr->inet->ipv4 = saddr->inet->has_ipv4 = has_ipv4; - saddr->inet->ipv6 = saddr->inet->has_ipv6 = has_ipv6; + saddr->u.inet->ipv4 = saddr->u.inet->has_ipv4 = has_ipv4; + saddr->u.inet->ipv6 = saddr->u.inet->has_ipv6 = has_ipv6; if (vs->ws_enabled) { - wsaddr->kind = SOCKET_ADDRESS_KIND_INET; - wsaddr->inet = g_new0(InetSocketAddress, 1); - wsaddr->inet->host = g_strdup(saddr->inet->host); - wsaddr->inet->port = g_strdup(websocket); + wsaddr->type = SOCKET_ADDRESS_KIND_INET; + wsaddr->u.inet = g_new0(InetSocketAddress, 1); + wsaddr->u.inet->host = g_strdup(saddr->u.inet->host); + wsaddr->u.inet->port = g_strdup(websocket); if (to) { - wsaddr->inet->has_to = true; - wsaddr->inet->to = to; + wsaddr->u.inet->has_to = true; + wsaddr->u.inet->to = to; } - wsaddr->inet->ipv4 = wsaddr->inet->has_ipv4 = has_ipv4; - wsaddr->inet->ipv6 = wsaddr->inet->has_ipv6 = has_ipv6; + wsaddr->u.inet->ipv4 = wsaddr->u.inet->has_ipv4 = has_ipv4; + wsaddr->u.inet->ipv6 = wsaddr->u.inet->has_ipv6 = has_ipv6; } } } else { @@ -3770,7 +3770,7 @@ void vnc_display_open(const char *id, Error **errp) if (csock < 0) { goto fail; } - vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX; + vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; vnc_connect(vs, csock, false, false); } else { /* listen for connects */ @@ -3778,7 +3778,7 @@ void vnc_display_open(const char *id, Error **errp) if (vs->lsock < 0) { goto fail; } - vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX; + vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; if (vs->ws_enabled) { vs->lwebsock = socket_listen(wsaddr, errp); if (vs->lwebsock < 0) { diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 9142917be5..dfe45875f8 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -918,23 +918,23 @@ SocketAddress *socket_parse(const char *str, Error **errp) error_setg(errp, "invalid Unix socket address"); goto fail; } else { - addr->kind = SOCKET_ADDRESS_KIND_UNIX; - addr->q_unix = g_new(UnixSocketAddress, 1); - addr->q_unix->path = g_strdup(str + 5); + addr->type = SOCKET_ADDRESS_KIND_UNIX; + addr->u.q_unix = g_new(UnixSocketAddress, 1); + addr->u.q_unix->path = g_strdup(str + 5); } } else if (strstart(str, "fd:", NULL)) { if (str[3] == '\0') { error_setg(errp, "invalid file descriptor address"); goto fail; } else { - addr->kind = SOCKET_ADDRESS_KIND_FD; - addr->fd = g_new(String, 1); - addr->fd->str = g_strdup(str + 3); + addr->type = SOCKET_ADDRESS_KIND_FD; + addr->u.fd = g_new(String, 1); + addr->u.fd->str = g_strdup(str + 3); } } else { - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = inet_parse(str, errp); - if (addr->inet == NULL) { + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet = inet_parse(str, errp); + if (addr->u.inet == NULL) { goto fail; } } @@ -952,19 +952,19 @@ int socket_connect(SocketAddress *addr, Error **errp, int fd; opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - switch (addr->kind) { + switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: - inet_addr_to_opts(opts, addr->inet); + inet_addr_to_opts(opts, addr->u.inet); fd = inet_connect_opts(opts, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_UNIX: - qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); + qemu_opt_set(opts, "path", addr->u.q_unix->path, &error_abort); fd = unix_connect_opts(opts, errp, callback, opaque); break; case SOCKET_ADDRESS_KIND_FD: - fd = monitor_get_fd(cur_mon, addr->fd->str, errp); + fd = monitor_get_fd(cur_mon, addr->u.fd->str, errp); if (fd >= 0 && callback) { qemu_set_nonblock(fd); callback(fd, NULL, opaque); @@ -984,19 +984,19 @@ int socket_listen(SocketAddress *addr, Error **errp) int fd; opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - switch (addr->kind) { + switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: - inet_addr_to_opts(opts, addr->inet); + inet_addr_to_opts(opts, addr->u.inet); fd = inet_listen_opts(opts, 0, errp); break; case SOCKET_ADDRESS_KIND_UNIX: - qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort); + qemu_opt_set(opts, "path", addr->u.q_unix->path, &error_abort); fd = unix_listen_opts(opts, errp); break; case SOCKET_ADDRESS_KIND_FD: - fd = monitor_get_fd(cur_mon, addr->fd->str, errp); + fd = monitor_get_fd(cur_mon, addr->u.fd->str, errp); break; default: @@ -1012,12 +1012,12 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp) int fd; opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort); - switch (remote->kind) { + switch (remote->type) { case SOCKET_ADDRESS_KIND_INET: - inet_addr_to_opts(opts, remote->inet); + inet_addr_to_opts(opts, remote->u.inet); if (local) { - qemu_opt_set(opts, "localaddr", local->inet->host, &error_abort); - qemu_opt_set(opts, "localport", local->inet->port, &error_abort); + qemu_opt_set(opts, "localaddr", local->u.inet->host, &error_abort); + qemu_opt_set(opts, "localport", local->u.inet->port, &error_abort); } fd = inet_dgram_opts(opts, errp); break; @@ -1052,14 +1052,14 @@ socket_sockaddr_to_address_inet(struct sockaddr_storage *sa, } addr = g_new0(SocketAddress, 1); - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(host); - addr->inet->port = g_strdup(serv); + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet = g_new0(InetSocketAddress, 1); + addr->u.inet->host = g_strdup(host); + addr->u.inet->port = g_strdup(serv); if (sa->ss_family == AF_INET) { - addr->inet->has_ipv4 = addr->inet->ipv4 = true; + addr->u.inet->has_ipv4 = addr->u.inet->ipv4 = true; } else { - addr->inet->has_ipv6 = addr->inet->ipv6 = true; + addr->u.inet->has_ipv6 = addr->u.inet->ipv6 = true; } return addr; @@ -1076,11 +1076,11 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, struct sockaddr_un *su = (struct sockaddr_un *)sa; addr = g_new0(SocketAddress, 1); - addr->kind = SOCKET_ADDRESS_KIND_UNIX; - addr->q_unix = g_new0(UnixSocketAddress, 1); + addr->type = SOCKET_ADDRESS_KIND_UNIX; + addr->u.q_unix = g_new0(UnixSocketAddress, 1); if (su->sun_path[0]) { - addr->q_unix->path = g_strndup(su->sun_path, - sizeof(su->sun_path)); + addr->u.q_unix->path = g_strndup(su->sun_path, + sizeof(su->sun_path)); } return addr; From 8d0bcba8370a4e8606dee602393a14d0c48e8bfc Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:56 -0600 Subject: [PATCH 18/25] net: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for net-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-18-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- net/dump.c | 4 ++-- net/hub.c | 4 ++-- net/l2tpv3.c | 4 ++-- net/net.c | 24 ++++++++++++------------ net/slirp.c | 4 ++-- net/socket.c | 4 ++-- net/tap-win32.c | 4 ++-- net/tap.c | 8 ++++---- net/vde.c | 4 ++-- net/vhost-user.c | 4 ++-- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/net/dump.c b/net/dump.c index dd0555f8bd..ce16a4b0e3 100644 --- a/net/dump.c +++ b/net/dump.c @@ -187,8 +187,8 @@ int net_init_dump(const NetClientOptions *opts, const char *name, NetClientState *nc; DumpNetClient *dnc; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP); - dump = opts->dump; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_DUMP); + dump = opts->u.dump; assert(peer); diff --git a/net/hub.c b/net/hub.c index 3047f12766..9ae9f012cb 100644 --- a/net/hub.c +++ b/net/hub.c @@ -285,9 +285,9 @@ int net_init_hubport(const NetClientOptions *opts, const char *name, { const NetdevHubPortOptions *hubport; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT); + assert(opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT); assert(!peer); - hubport = opts->hubport; + hubport = opts->u.hubport; net_hub_add_port(hubport->hubid, name); return 0; diff --git a/net/l2tpv3.c b/net/l2tpv3.c index 4f9bceecc9..8e68e540ec 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -545,8 +545,8 @@ int net_init_l2tpv3(const NetClientOptions *opts, s->queue_tail = 0; s->header_mismatch = false; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_L2TPV3); - l2tpv3 = opts->l2tpv3; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_L2TPV3); + l2tpv3 = opts->u.l2tpv3; if (l2tpv3->has_ipv6 && l2tpv3->ipv6) { s->ipv6 = l2tpv3->ipv6; diff --git a/net/net.c b/net/net.c index a3e9d1a9b3..ade6051846 100644 --- a/net/net.c +++ b/net/net.c @@ -882,8 +882,8 @@ static int net_init_nic(const NetClientOptions *opts, const char *name, NICInfo *nd; const NetLegacyNicOptions *nic; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC); - nic = opts->nic; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_NIC); + nic = opts->u.nic; idx = nic_get_free_idx(); if (idx == -1 || nb_nics >= MAX_NICS) { @@ -984,9 +984,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) opts = netdev->opts; name = netdev->id; - if (opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP || - opts->kind == NET_CLIENT_OPTIONS_KIND_NIC || - !net_client_init_fun[opts->kind]) { + if (opts->type == NET_CLIENT_OPTIONS_KIND_DUMP || + opts->type == NET_CLIENT_OPTIONS_KIND_NIC || + !net_client_init_fun[opts->type]) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", "a netdev backend type"); return -1; @@ -997,16 +997,16 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) /* missing optional values have been initialized to "all bits zero" */ name = net->has_id ? net->id : net->name; - if (opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) { + if (opts->type == NET_CLIENT_OPTIONS_KIND_NONE) { return 0; /* nothing to do */ } - if (opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) { + if (opts->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", "a net type"); return -1; } - if (!net_client_init_fun[opts->kind]) { + if (!net_client_init_fun[opts->type]) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", "a net backend type (maybe it is not compiled " "into this binary)"); @@ -1014,17 +1014,17 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) } /* Do not add to a vlan if it's a nic with a netdev= parameter. */ - if (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC || - !opts->nic->has_netdev) { + if (opts->type != NET_CLIENT_OPTIONS_KIND_NIC || + !opts->u.nic->has_netdev) { peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL); } } - if (net_client_init_fun[opts->kind](opts, name, peer, errp) < 0) { + if (net_client_init_fun[opts->type](opts, name, peer, errp) < 0) { /* FIXME drop when all init functions store an Error */ if (errp && !*errp) { error_setg(errp, QERR_DEVICE_INIT_FAILED, - NetClientOptionsKind_lookup[opts->kind]); + NetClientOptionsKind_lookup[opts->type]); } return -1; } diff --git a/net/slirp.c b/net/slirp.c index 7657b38fdf..f505570adb 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -746,8 +746,8 @@ int net_init_slirp(const NetClientOptions *opts, const char *name, const NetdevUserOptions *user; const char **dnssearch; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER); - user = opts->user; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER); + user = opts->u.user; vnet = user->has_net ? g_strdup(user->net) : user->has_ip ? g_strdup_printf("%s/24", user->ip) : diff --git a/net/socket.c b/net/socket.c index b1e3b1c8d9..e8605d4ded 100644 --- a/net/socket.c +++ b/net/socket.c @@ -706,8 +706,8 @@ int net_init_socket(const NetClientOptions *opts, const char *name, Error *err = NULL; const NetdevSocketOptions *sock; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET); - sock = opts->socket; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_SOCKET); + sock = opts->u.socket; if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast + sock->has_udp != 1) { diff --git a/net/tap-win32.c b/net/tap-win32.c index 625d53c64b..4e2fa55006 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -767,8 +767,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name, /* FIXME error_setg(errp, ...) on failure */ const NetdevTapOptions *tap; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); - tap = opts->tap; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP); + tap = opts->u.tap; if (!tap->has_ifname) { error_report("tap: no interface name"); diff --git a/net/tap.c b/net/tap.c index bd01590e8e..85c4142d15 100644 --- a/net/tap.c +++ b/net/tap.c @@ -565,8 +565,8 @@ int net_init_bridge(const NetClientOptions *opts, const char *name, TAPState *s; int fd, vnet_hdr; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE); - bridge = opts->bridge; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE); + bridge = opts->u.bridge; helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER; br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE; @@ -728,8 +728,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name, const char *vhostfdname; char ifname[128]; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); - tap = opts->tap; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP); + tap = opts->u.tap; queues = tap->has_queues ? tap->queues : 1; vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; diff --git a/net/vde.c b/net/vde.c index dacaa64b47..4475d929e6 100644 --- a/net/vde.c +++ b/net/vde.c @@ -115,8 +115,8 @@ int net_init_vde(const NetClientOptions *opts, const char *name, /* FIXME error_setg(errp, ...) on failure */ const NetdevVdeOptions *vde; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE); - vde = opts->vde; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_VDE); + vde = opts->u.vde; /* missing optional values have been initialized to "all bits zero" */ if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group, diff --git a/net/vhost-user.c b/net/vhost-user.c index 17b5c2a722..0ebd7df528 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -301,8 +301,8 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, const NetdevVhostUserOptions *vhost_user_opts; CharDriverState *chr; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER); - vhost_user_opts = opts->vhost_user; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + vhost_user_opts = opts->u.vhost_user; chr = net_vhost_parse_chardev(vhost_user_opts, errp); if (!chr) { From 130257dc443574a9da91dc293665be2cfc40245a Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:57 -0600 Subject: [PATCH 19/25] char: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for character-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-19-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- qemu-char.c | 164 +++++++++++++++++++++++----------------------- spice-qemu-char.c | 12 ++-- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index c4eb4eea31..5448b0f30b 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -97,18 +97,18 @@ static int SocketAddress_to_str(char *dest, int max_len, const char *prefix, SocketAddress *addr, bool is_listen, bool is_telnet) { - switch (addr->kind) { + switch (addr->type) { case SOCKET_ADDRESS_KIND_INET: return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix, - is_telnet ? "telnet" : "tcp", addr->inet->host, - addr->inet->port, is_listen ? ",server" : ""); + is_telnet ? "telnet" : "tcp", addr->u.inet->host, + addr->u.inet->port, is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_KIND_UNIX: return snprintf(dest, max_len, "%sunix:%s%s", prefix, - addr->q_unix->path, is_listen ? ",server" : ""); + addr->u.q_unix->path, is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_KIND_FD: - return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str, + return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->u.fd->str, is_listen ? ",server" : ""); break; default: @@ -661,7 +661,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id, ChardevBackend *backend, ChardevReturn *ret, Error **errp) { - ChardevMux *mux = backend->mux; + ChardevMux *mux = backend->u.mux; CharDriverState *chr, *drv; MuxDriver *d; @@ -1070,7 +1070,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id, ChardevReturn *ret, Error **errp) { - ChardevHostdev *opts = backend->pipe; + ChardevHostdev *opts = backend->u.pipe; int fd_in, fd_out; char filename_in[CHR_MAX_FILENAME_SIZE]; char filename_out[CHR_MAX_FILENAME_SIZE]; @@ -1148,7 +1148,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, ChardevReturn *ret, Error **errp) { - ChardevStdio *opts = backend->stdio; + ChardevStdio *opts = backend->u.stdio; CharDriverState *chr; struct sigaction act; @@ -2147,7 +2147,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id, ChardevReturn *ret, Error **errp) { - ChardevHostdev *opts = backend->pipe; + ChardevHostdev *opts = backend->u.pipe; const char *filename = opts->device; CharDriverState *chr; WinCharState *s; @@ -3202,7 +3202,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id, ChardevReturn *ret, Error **errp) { - ChardevRingbuf *opts = backend->ringbuf; + ChardevRingbuf *opts = backend->u.ringbuf; CharDriverState *chr; RingBufCharDriver *d; @@ -3477,16 +3477,16 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: file: no filename given"); return; } - backend->file = g_new0(ChardevFile, 1); - backend->file->out = g_strdup(path); + backend->u.file = g_new0(ChardevFile, 1); + backend->u.file->out = g_strdup(path); } static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, Error **errp) { - backend->stdio = g_new0(ChardevStdio, 1); - backend->stdio->has_signal = true; - backend->stdio->signal = qemu_opt_get_bool(opts, "signal", true); + backend->u.stdio = g_new0(ChardevStdio, 1); + backend->u.stdio->has_signal = true; + backend->u.stdio->signal = qemu_opt_get_bool(opts, "signal", true); } #ifdef HAVE_CHARDEV_SERIAL @@ -3499,8 +3499,8 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: serial/tty: no device path given"); return; } - backend->serial = g_new0(ChardevHostdev, 1); - backend->serial->device = g_strdup(device); + backend->u.serial = g_new0(ChardevHostdev, 1); + backend->u.serial->device = g_strdup(device); } #endif @@ -3514,8 +3514,8 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: parallel: no device path given"); return; } - backend->parallel = g_new0(ChardevHostdev, 1); - backend->parallel->device = g_strdup(device); + backend->u.parallel = g_new0(ChardevHostdev, 1); + backend->u.parallel->device = g_strdup(device); } #endif @@ -3528,8 +3528,8 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: pipe: no device path given"); return; } - backend->pipe = g_new0(ChardevHostdev, 1); - backend->pipe->device = g_strdup(device); + backend->u.pipe = g_new0(ChardevHostdev, 1); + backend->u.pipe->device = g_strdup(device); } static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, @@ -3537,12 +3537,12 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, { int val; - backend->ringbuf = g_new0(ChardevRingbuf, 1); + backend->u.ringbuf = g_new0(ChardevRingbuf, 1); val = qemu_opt_get_size(opts, "size", 0); if (val != 0) { - backend->ringbuf->has_size = true; - backend->ringbuf->size = val; + backend->u.ringbuf->has_size = true; + backend->u.ringbuf->size = val; } } @@ -3555,8 +3555,8 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: mux: no chardev given"); return; } - backend->mux = g_new0(ChardevMux, 1); - backend->mux->chardev = g_strdup(chardev); + backend->u.mux = g_new0(ChardevMux, 1); + backend->u.mux->chardev = g_strdup(chardev); } static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, @@ -3583,37 +3583,37 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, } } - backend->socket = g_new0(ChardevSocket, 1); + backend->u.socket = g_new0(ChardevSocket, 1); - backend->socket->has_nodelay = true; - backend->socket->nodelay = do_nodelay; - backend->socket->has_server = true; - backend->socket->server = is_listen; - backend->socket->has_telnet = true; - backend->socket->telnet = is_telnet; - backend->socket->has_wait = true; - backend->socket->wait = is_waitconnect; - backend->socket->has_reconnect = true; - backend->socket->reconnect = reconnect; + backend->u.socket->has_nodelay = true; + backend->u.socket->nodelay = do_nodelay; + backend->u.socket->has_server = true; + backend->u.socket->server = is_listen; + backend->u.socket->has_telnet = true; + backend->u.socket->telnet = is_telnet; + backend->u.socket->has_wait = true; + backend->u.socket->wait = is_waitconnect; + backend->u.socket->has_reconnect = true; + backend->u.socket->reconnect = reconnect; addr = g_new0(SocketAddress, 1); if (path) { - addr->kind = SOCKET_ADDRESS_KIND_UNIX; - addr->q_unix = g_new0(UnixSocketAddress, 1); - addr->q_unix->path = g_strdup(path); + addr->type = SOCKET_ADDRESS_KIND_UNIX; + addr->u.q_unix = g_new0(UnixSocketAddress, 1); + addr->u.q_unix->path = g_strdup(path); } else { - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(host); - addr->inet->port = g_strdup(port); - addr->inet->has_to = qemu_opt_get(opts, "to"); - addr->inet->to = qemu_opt_get_number(opts, "to", 0); - addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4"); - addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0); - addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6"); - addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0); + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet = g_new0(InetSocketAddress, 1); + addr->u.inet->host = g_strdup(host); + addr->u.inet->port = g_strdup(port); + addr->u.inet->has_to = qemu_opt_get(opts, "to"); + addr->u.inet->to = qemu_opt_get_number(opts, "to", 0); + addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4"); + addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0); + addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6"); + addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0); } - backend->socket->addr = addr; + backend->u.socket->addr = addr; } static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, @@ -3644,27 +3644,27 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, has_local = true; } - backend->udp = g_new0(ChardevUdp, 1); + backend->u.udp = g_new0(ChardevUdp, 1); addr = g_new0(SocketAddress, 1); - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(host); - addr->inet->port = g_strdup(port); - addr->inet->has_ipv4 = qemu_opt_get(opts, "ipv4"); - addr->inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0); - addr->inet->has_ipv6 = qemu_opt_get(opts, "ipv6"); - addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0); - backend->udp->remote = addr; + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet = g_new0(InetSocketAddress, 1); + addr->u.inet->host = g_strdup(host); + addr->u.inet->port = g_strdup(port); + addr->u.inet->has_ipv4 = qemu_opt_get(opts, "ipv4"); + addr->u.inet->ipv4 = qemu_opt_get_bool(opts, "ipv4", 0); + addr->u.inet->has_ipv6 = qemu_opt_get(opts, "ipv6"); + addr->u.inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0); + backend->u.udp->remote = addr; if (has_local) { - backend->udp->has_local = true; + backend->u.udp->has_local = true; addr = g_new0(SocketAddress, 1); - addr->kind = SOCKET_ADDRESS_KIND_INET; - addr->inet = g_new0(InetSocketAddress, 1); - addr->inet->host = g_strdup(localaddr); - addr->inet->port = g_strdup(localport); - backend->udp->local = addr; + addr->type = SOCKET_ADDRESS_KIND_INET; + addr->u.inet = g_new0(InetSocketAddress, 1); + addr->u.inet->host = g_strdup(localaddr); + addr->u.inet->port = g_strdup(localport); + backend->u.udp->local = addr; } } @@ -3737,7 +3737,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, } chr = NULL; - backend->kind = cd->kind; + backend->type = cd->kind; if (cd->parse) { cd->parse(opts, backend, &local_err); if (local_err) { @@ -3754,9 +3754,9 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, qapi_free_ChardevBackend(backend); qapi_free_ChardevReturn(ret); backend = g_new0(ChardevBackend, 1); - backend->mux = g_new0(ChardevMux, 1); - backend->kind = CHARDEV_BACKEND_KIND_MUX; - backend->mux->chardev = g_strdup(bid); + backend->u.mux = g_new0(ChardevMux, 1); + backend->type = CHARDEV_BACKEND_KIND_MUX; + backend->u.mux->chardev = g_strdup(bid); ret = qmp_chardev_add(id, backend, errp); if (!ret) { chr = qemu_chr_find(bid); @@ -4048,7 +4048,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, ChardevReturn *ret, Error **errp) { - ChardevFile *file = backend->file; + ChardevFile *file = backend->u.file; HANDLE out; if (file->has_in) { @@ -4070,7 +4070,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id, ChardevReturn *ret, Error **errp) { - ChardevHostdev *serial = backend->serial; + ChardevHostdev *serial = backend->u.serial; return qemu_chr_open_win_path(serial->device, errp); } @@ -4093,7 +4093,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, ChardevReturn *ret, Error **errp) { - ChardevFile *file = backend->file; + ChardevFile *file = backend->u.file; int flags, in = -1, out; flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY; @@ -4120,7 +4120,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id, ChardevReturn *ret, Error **errp) { - ChardevHostdev *serial = backend->serial; + ChardevHostdev *serial = backend->u.serial; int fd; fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); @@ -4138,7 +4138,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id, ChardevReturn *ret, Error **errp) { - ChardevHostdev *parallel = backend->parallel; + ChardevHostdev *parallel = backend->u.parallel; int fd; fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); @@ -4183,7 +4183,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, { CharDriverState *chr; TCPCharDriver *s; - ChardevSocket *sock = backend->socket; + ChardevSocket *sock = backend->u.socket; SocketAddress *addr = sock->addr; bool do_nodelay = sock->has_nodelay ? sock->nodelay : false; bool is_listen = sock->has_server ? sock->server : true; @@ -4196,7 +4196,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, s->fd = -1; s->listen_fd = -1; - s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX; + s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX; s->is_listen = is_listen; s->is_telnet = is_telnet; s->do_nodelay = do_nodelay; @@ -4250,7 +4250,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id, ChardevReturn *ret, Error **errp) { - ChardevUdp *udp = backend->udp; + ChardevUdp *udp = backend->u.udp; int fd; fd = socket_dgram(udp->remote, udp->local, errp); @@ -4279,7 +4279,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, for (i = backends; i; i = i->next) { cd = i->data; - if (cd->kind == backend->kind) { + if (cd->kind == backend->type) { chr = cd->create(id, backend, ret, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -4297,9 +4297,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, chr->label = g_strdup(id); chr->avail_connections = - (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; + (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; if (!chr->filename) { - chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]); + chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]); } if (!chr->explicit_be_open) { qemu_chr_be_event(chr, CHR_EVENT_OPENED); diff --git a/spice-qemu-char.c b/spice-qemu-char.c index a20fb5c90c..e70e0f7366 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -301,7 +301,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id, ChardevReturn *ret, Error **errp) { - const char *type = backend->spicevmc->type; + const char *type = backend->u.spicevmc->type; const char **psubtype = spice_server_char_device_recognized_subtypes(); for (; *psubtype != NULL; ++psubtype) { @@ -324,7 +324,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id, ChardevReturn *ret, Error **errp) { - const char *name = backend->spiceport->fqdn; + const char *name = backend->u.spiceport->fqdn; CharDriverState *chr; SpiceCharDriver *s; @@ -362,8 +362,8 @@ static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: spice channel: no name given"); return; } - backend->spicevmc = g_new0(ChardevSpiceChannel, 1); - backend->spicevmc->type = g_strdup(name); + backend->u.spicevmc = g_new0(ChardevSpiceChannel, 1); + backend->u.spicevmc->type = g_strdup(name); } static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, @@ -375,8 +375,8 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, error_setg(errp, "chardev: spice port: no name given"); return; } - backend->spiceport = g_new0(ChardevSpicePort, 1); - backend->spiceport->fqdn = g_strdup(name); + backend->u.spiceport = g_new0(ChardevSpicePort, 1); + backend->u.spiceport->fqdn = g_strdup(name); } static void register_types(void) From 568c73a4783cd981e9aa6de4f15dcda7829643ad Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:58 -0600 Subject: [PATCH 20/25] input: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for input-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-20-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- hmp.c | 8 ++-- hw/char/escc.c | 12 +++--- hw/input/hid.c | 32 +++++++------- hw/input/ps2.c | 24 +++++------ hw/input/virtio-input-hid.c | 27 ++++++------ ui/console.c | 20 ++++----- ui/input-keymap.c | 20 ++++----- ui/input-legacy.c | 21 +++++----- ui/input.c | 84 ++++++++++++++++++------------------- 9 files changed, 125 insertions(+), 123 deletions(-) diff --git a/hmp.c b/hmp.c index 88fd804a9e..cc4946d6ee 100644 --- a/hmp.c +++ b/hmp.c @@ -1735,15 +1735,15 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) if (*endp != '\0') { goto err_out; } - keylist->value->kind = KEY_VALUE_KIND_NUMBER; - keylist->value->number = value; + keylist->value->type = KEY_VALUE_KIND_NUMBER; + keylist->value->u.number = value; } else { int idx = index_from_key(keyname_buf); if (idx == Q_KEY_CODE_MAX) { goto err_out; } - keylist->value->kind = KEY_VALUE_KIND_QCODE; - keylist->value->qcode = idx; + keylist->value->type = KEY_VALUE_KIND_QCODE; + keylist->value->u.qcode = idx; } if (!separator) { diff --git a/hw/char/escc.c b/hw/char/escc.c index 9816154206..c9840e11da 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -842,13 +842,13 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, ChannelState *s = (ChannelState *)dev; int qcode, keycode; - assert(evt->kind == INPUT_EVENT_KIND_KEY); - qcode = qemu_input_key_value_to_qcode(evt->key->key); + assert(evt->type == INPUT_EVENT_KIND_KEY); + qcode = qemu_input_key_value_to_qcode(evt->u.key->key); trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode], - evt->key->down); + evt->u.key->down); if (qcode == Q_KEY_CODE_CAPS_LOCK) { - if (evt->key->down) { + if (evt->u.key->down) { s->caps_lock_mode ^= 1; if (s->caps_lock_mode == 2) { return; /* Drop second press */ @@ -862,7 +862,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } if (qcode == Q_KEY_CODE_NUM_LOCK) { - if (evt->key->down) { + if (evt->u.key->down) { s->num_lock_mode ^= 1; if (s->num_lock_mode == 2) { return; /* Drop second press */ @@ -876,7 +876,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, } keycode = qcode_to_keycode[qcode]; - if (!evt->key->down) { + if (!evt->u.key->down) { keycode |= 0x80; } trace_escc_sunkbd_event_out(keycode); diff --git a/hw/input/hid.c b/hw/input/hid.c index 21ebd9e718..e39269fc7a 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -119,33 +119,33 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src, assert(hs->n < QUEUE_LENGTH); e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_REL: - if (evt->rel->axis == INPUT_AXIS_X) { - e->xdx += evt->rel->value; - } else if (evt->rel->axis == INPUT_AXIS_Y) { - e->ydy += evt->rel->value; + if (evt->u.rel->axis == INPUT_AXIS_X) { + e->xdx += evt->u.rel->value; + } else if (evt->u.rel->axis == INPUT_AXIS_Y) { + e->ydy += evt->u.rel->value; } break; case INPUT_EVENT_KIND_ABS: - if (evt->rel->axis == INPUT_AXIS_X) { - e->xdx = evt->rel->value; - } else if (evt->rel->axis == INPUT_AXIS_Y) { - e->ydy = evt->rel->value; + if (evt->u.rel->axis == INPUT_AXIS_X) { + e->xdx = evt->u.rel->value; + } else if (evt->u.rel->axis == INPUT_AXIS_Y) { + e->ydy = evt->u.rel->value; } break; case INPUT_EVENT_KIND_BTN: - if (evt->btn->down) { - e->buttons_state |= bmap[evt->btn->button]; - if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + if (evt->u.btn->down) { + e->buttons_state |= bmap[evt->u.btn->button]; + if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) { e->dz--; - } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) { e->dz++; } } else { - e->buttons_state &= ~bmap[evt->btn->button]; + e->buttons_state &= ~bmap[evt->u.btn->button]; } break; @@ -223,8 +223,8 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, int scancodes[3], i, count; int slot; - count = qemu_input_key_value_to_scancode(evt->key->key, - evt->key->down, + count = qemu_input_key_value_to_scancode(evt->u.key->key, + evt->u.key->down, scancodes); if (hs->n + count > QUEUE_LENGTH) { fprintf(stderr, "usb-kbd: warning: key event queue full\n"); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index fdbe565e62..3d6d4961db 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -183,8 +183,8 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, int scancodes[3], i, count; qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - count = qemu_input_key_value_to_scancode(evt->key->key, - evt->key->down, + count = qemu_input_key_value_to_scancode(evt->u.key->key, + evt->u.key->down, scancodes); for (i = 0; i < count; i++) { ps2_put_keycode(s, scancodes[i]); @@ -393,25 +393,25 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) return; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_REL: - if (evt->rel->axis == INPUT_AXIS_X) { - s->mouse_dx += evt->rel->value; - } else if (evt->rel->axis == INPUT_AXIS_Y) { - s->mouse_dy -= evt->rel->value; + if (evt->u.rel->axis == INPUT_AXIS_X) { + s->mouse_dx += evt->u.rel->value; + } else if (evt->u.rel->axis == INPUT_AXIS_Y) { + s->mouse_dy -= evt->u.rel->value; } break; case INPUT_EVENT_KIND_BTN: - if (evt->btn->down) { - s->mouse_buttons |= bmap[evt->btn->button]; - if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + if (evt->u.btn->down) { + s->mouse_buttons |= bmap[evt->u.btn->button]; + if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) { s->mouse_dz--; - } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) { s->mouse_dz++; } } else { - s->mouse_buttons &= ~bmap[evt->btn->button]; + s->mouse_buttons &= ~bmap[evt->u.btn->button]; } break; diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index 4d85dad4d1..bdd479cd0a 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -191,44 +191,45 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, virtio_input_event event; int qcode; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_KEY: - qcode = qemu_input_key_value_to_qcode(evt->key->key); + qcode = qemu_input_key_value_to_qcode(evt->u.key->key); if (qcode && keymap_qcode[qcode]) { event.type = cpu_to_le16(EV_KEY); event.code = cpu_to_le16(keymap_qcode[qcode]); - event.value = cpu_to_le32(evt->key->down ? 1 : 0); + event.value = cpu_to_le32(evt->u.key->down ? 1 : 0); virtio_input_send(vinput, &event); } else { - if (evt->key->down) { + if (evt->u.key->down) { fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__, qcode, QKeyCode_lookup[qcode]); } } break; case INPUT_EVENT_KIND_BTN: - if (keymap_button[evt->btn->button]) { + if (keymap_button[evt->u.btn->button]) { event.type = cpu_to_le16(EV_KEY); - event.code = cpu_to_le16(keymap_button[evt->btn->button]); - event.value = cpu_to_le32(evt->btn->down ? 1 : 0); + event.code = cpu_to_le16(keymap_button[evt->u.btn->button]); + event.value = cpu_to_le32(evt->u.btn->down ? 1 : 0); virtio_input_send(vinput, &event); } else { - if (evt->btn->down) { + if (evt->u.btn->down) { fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__, - evt->btn->button, InputButton_lookup[evt->btn->button]); + evt->u.btn->button, + InputButton_lookup[evt->u.btn->button]); } } break; case INPUT_EVENT_KIND_REL: event.type = cpu_to_le16(EV_REL); - event.code = cpu_to_le16(axismap_rel[evt->rel->axis]); - event.value = cpu_to_le32(evt->rel->value); + event.code = cpu_to_le16(axismap_rel[evt->u.rel->axis]); + event.value = cpu_to_le32(evt->u.rel->value); virtio_input_send(vinput, &event); break; case INPUT_EVENT_KIND_ABS: event.type = cpu_to_le16(EV_ABS); - event.code = cpu_to_le16(axismap_abs[evt->abs->axis]); - event.value = cpu_to_le32(evt->abs->value); + event.code = cpu_to_le16(axismap_abs[evt->u.abs->axis]); + event.value = cpu_to_le32(evt->u.abs->value); virtio_input_send(vinput, &event); break; default: diff --git a/ui/console.c b/ui/console.c index cf649b2612..f26544eb26 100644 --- a/ui/console.c +++ b/ui/console.c @@ -2016,7 +2016,7 @@ static VcHandler *vc_handler = text_console_init; static CharDriverState *vc_init(const char *id, ChardevBackend *backend, ChardevReturn *ret, Error **errp) { - return vc_handler(backend->vc, errp); + return vc_handler(backend->u.vc, errp); } void register_vc_handler(VcHandler *handler) @@ -2057,30 +2057,30 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, { int val; - backend->vc = g_new0(ChardevVC, 1); + backend->u.vc = g_new0(ChardevVC, 1); val = qemu_opt_get_number(opts, "width", 0); if (val != 0) { - backend->vc->has_width = true; - backend->vc->width = val; + backend->u.vc->has_width = true; + backend->u.vc->width = val; } val = qemu_opt_get_number(opts, "height", 0); if (val != 0) { - backend->vc->has_height = true; - backend->vc->height = val; + backend->u.vc->has_height = true; + backend->u.vc->height = val; } val = qemu_opt_get_number(opts, "cols", 0); if (val != 0) { - backend->vc->has_cols = true; - backend->vc->cols = val; + backend->u.vc->has_cols = true; + backend->u.vc->cols = val; } val = qemu_opt_get_number(opts, "rows", 0); if (val != 0) { - backend->vc->has_rows = true; - backend->vc->rows = val; + backend->u.vc->has_rows = true; + backend->u.vc->rows = val; } } diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 7635cb0dc4..d36be4b60d 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -139,11 +139,11 @@ static int number_to_qcode[0x100]; int qemu_input_key_value_to_number(const KeyValue *value) { - if (value->kind == KEY_VALUE_KIND_QCODE) { - return qcode_to_number[value->qcode]; + if (value->type == KEY_VALUE_KIND_QCODE) { + return qcode_to_number[value->u.qcode]; } else { - assert(value->kind == KEY_VALUE_KIND_NUMBER); - return value->number; + assert(value->type == KEY_VALUE_KIND_NUMBER); + return value->u.number; } } @@ -166,11 +166,11 @@ int qemu_input_key_number_to_qcode(uint8_t nr) int qemu_input_key_value_to_qcode(const KeyValue *value) { - if (value->kind == KEY_VALUE_KIND_QCODE) { - return value->qcode; + if (value->type == KEY_VALUE_KIND_QCODE) { + return value->u.qcode; } else { - assert(value->kind == KEY_VALUE_KIND_NUMBER); - return qemu_input_key_number_to_qcode(value->number); + assert(value->type == KEY_VALUE_KIND_NUMBER); + return qemu_input_key_number_to_qcode(value->u.number); } } @@ -180,8 +180,8 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, int keycode = qemu_input_key_value_to_number(value); int count = 0; - if (value->kind == KEY_VALUE_KIND_QCODE && - value->qcode == Q_KEY_CODE_PAUSE) { + if (value->type == KEY_VALUE_KIND_QCODE && + value->u.qcode == Q_KEY_CODE_PAUSE) { /* specific case */ int v = down ? 0 : 0x80; codes[count++] = 0xe1; diff --git a/ui/input-legacy.c b/ui/input-legacy.c index e50f2968e1..a67ed329ce 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -113,8 +113,8 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, if (!entry || !entry->put_kbd) { return; } - count = qemu_input_key_value_to_scancode(evt->key->key, - evt->key->down, + count = qemu_input_key_value_to_scancode(evt->u.key->key, + evt->u.key->down, scancodes); for (i = 0; i < count; i++) { entry->put_kbd(entry->opaque, scancodes[i]); @@ -150,21 +150,22 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, }; QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev; - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_BTN: - if (evt->btn->down) { - s->buttons |= bmap[evt->btn->button]; + if (evt->u.btn->down) { + s->buttons |= bmap[evt->u.btn->button]; } else { - s->buttons &= ~bmap[evt->btn->button]; + s->buttons &= ~bmap[evt->u.btn->button]; } - if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_UP) { + if (evt->u.btn->down && evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], -1, s->buttons); } - if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { + if (evt->u.btn->down && + evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) { s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, s->axis[INPUT_AXIS_X], s->axis[INPUT_AXIS_Y], @@ -173,10 +174,10 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, } break; case INPUT_EVENT_KIND_ABS: - s->axis[evt->abs->axis] = evt->abs->value; + s->axis[evt->u.abs->axis] = evt->u.abs->value; break; case INPUT_EVENT_KIND_REL: - s->axis[evt->rel->axis] += evt->rel->value; + s->axis[evt->u.rel->axis] += evt->u.rel->value; break; default: break; diff --git a/ui/input.c b/ui/input.c index 1a552d1de1..643f885edf 100644 --- a/ui/input.c +++ b/ui/input.c @@ -147,10 +147,10 @@ void qmp_x_input_send_event(bool has_console, int64_t console, for (e = events; e != NULL; e = e->next) { InputEvent *event = e->value; - if (!qemu_input_find_handler(1 << event->kind, con)) { + if (!qemu_input_find_handler(1 << event->type, con)) { error_setg(errp, "Input handler not found for " "event type %s", - InputEventKind_lookup[event->kind]); + InputEventKind_lookup[event->type]); return; } } @@ -168,22 +168,22 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt) { switch (graphic_rotate) { case 90: - if (evt->abs->axis == INPUT_AXIS_X) { - evt->abs->axis = INPUT_AXIS_Y; - } else if (evt->abs->axis == INPUT_AXIS_Y) { - evt->abs->axis = INPUT_AXIS_X; - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + if (evt->u.abs->axis == INPUT_AXIS_X) { + evt->u.abs->axis = INPUT_AXIS_Y; + } else if (evt->u.abs->axis == INPUT_AXIS_Y) { + evt->u.abs->axis = INPUT_AXIS_X; + evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value; } break; case 180: - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; + evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value; break; case 270: - if (evt->abs->axis == INPUT_AXIS_X) { - evt->abs->axis = INPUT_AXIS_Y; - evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value; - } else if (evt->abs->axis == INPUT_AXIS_Y) { - evt->abs->axis = INPUT_AXIS_X; + if (evt->u.abs->axis == INPUT_AXIS_X) { + evt->u.abs->axis = INPUT_AXIS_Y; + evt->u.abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->u.abs->value; + } else if (evt->u.abs->axis == INPUT_AXIS_Y) { + evt->u.abs->axis = INPUT_AXIS_X; } break; } @@ -197,18 +197,18 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) if (src) { idx = qemu_console_get_index(src); } - switch (evt->kind) { + switch (evt->type) { case INPUT_EVENT_KIND_KEY: - switch (evt->key->key->kind) { + switch (evt->u.key->key->type) { case KEY_VALUE_KIND_NUMBER: - qcode = qemu_input_key_number_to_qcode(evt->key->key->number); + qcode = qemu_input_key_number_to_qcode(evt->u.key->key->u.number); name = QKeyCode_lookup[qcode]; - trace_input_event_key_number(idx, evt->key->key->number, - name, evt->key->down); + trace_input_event_key_number(idx, evt->u.key->key->u.number, + name, evt->u.key->down); break; case KEY_VALUE_KIND_QCODE: - name = QKeyCode_lookup[evt->key->key->qcode]; - trace_input_event_key_qcode(idx, name, evt->key->down); + name = QKeyCode_lookup[evt->u.key->key->u.qcode]; + trace_input_event_key_qcode(idx, name, evt->u.key->down); break; case KEY_VALUE_KIND_MAX: /* keep gcc happy */ @@ -216,16 +216,16 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) } break; case INPUT_EVENT_KIND_BTN: - name = InputButton_lookup[evt->btn->button]; - trace_input_event_btn(idx, name, evt->btn->down); + name = InputButton_lookup[evt->u.btn->button]; + trace_input_event_btn(idx, name, evt->u.btn->down); break; case INPUT_EVENT_KIND_REL: - name = InputAxis_lookup[evt->rel->axis]; - trace_input_event_rel(idx, name, evt->rel->value); + name = InputAxis_lookup[evt->u.rel->axis]; + trace_input_event_rel(idx, name, evt->u.rel->value); break; case INPUT_EVENT_KIND_ABS: - name = InputAxis_lookup[evt->abs->axis]; - trace_input_event_abs(idx, name, evt->abs->value); + name = InputAxis_lookup[evt->u.abs->axis]; + trace_input_event_abs(idx, name, evt->u.abs->value); break; case INPUT_EVENT_KIND_MAX: /* keep gcc happy */ @@ -311,12 +311,12 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) qemu_input_event_trace(src, evt); /* pre processing */ - if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) { + if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) { qemu_input_transform_abs_rotate(evt); } /* send event */ - s = qemu_input_find_handler(1 << evt->kind, src); + s = qemu_input_find_handler(1 << evt->type, src); if (!s) { return; } @@ -348,10 +348,10 @@ void qemu_input_event_sync(void) InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) { InputEvent *evt = g_new0(InputEvent, 1); - evt->key = g_new0(InputKeyEvent, 1); - evt->kind = INPUT_EVENT_KIND_KEY; - evt->key->key = key; - evt->key->down = down; + evt->u.key = g_new0(InputKeyEvent, 1); + evt->type = INPUT_EVENT_KIND_KEY; + evt->u.key->key = key; + evt->u.key->down = down; return evt; } @@ -372,16 +372,16 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down) void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down) { KeyValue *key = g_new0(KeyValue, 1); - key->kind = KEY_VALUE_KIND_NUMBER; - key->number = num; + key->type = KEY_VALUE_KIND_NUMBER; + key->u.number = num; qemu_input_event_send_key(src, key, down); } void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down) { KeyValue *key = g_new0(KeyValue, 1); - key->kind = KEY_VALUE_KIND_QCODE; - key->qcode = q; + key->type = KEY_VALUE_KIND_QCODE; + key->u.qcode = q; qemu_input_event_send_key(src, key, down); } @@ -398,10 +398,10 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) InputEvent *qemu_input_event_new_btn(InputButton btn, bool down) { InputEvent *evt = g_new0(InputEvent, 1); - evt->btn = g_new0(InputBtnEvent, 1); - evt->kind = INPUT_EVENT_KIND_BTN; - evt->btn->button = btn; - evt->btn->down = down; + evt->u.btn = g_new0(InputBtnEvent, 1); + evt->type = INPUT_EVENT_KIND_BTN; + evt->u.btn->button = btn; + evt->u.btn->down = down; return evt; } @@ -451,8 +451,8 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind, InputEvent *evt = g_new0(InputEvent, 1); InputMoveEvent *move = g_new0(InputMoveEvent, 1); - evt->kind = kind; - evt->data = move; + evt->type = kind; + evt->u.data = move; move->axis = axis; move->value = value; return evt; From 1fd5d4fea4ba686705fd377c7cffc0f0c9f83f93 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:34:59 -0600 Subject: [PATCH 21/25] memory: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for memory-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-21-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- hmp.c | 6 +++--- hw/mem/pc-dimm.c | 6 +++--- numa.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hmp.c b/hmp.c index cc4946d6ee..39d5815697 100644 --- a/hmp.c +++ b/hmp.c @@ -1958,12 +1958,12 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) value = info->value; if (value) { - switch (value->kind) { + switch (value->type) { case MEMORY_DEVICE_INFO_KIND_DIMM: - di = value->dimm; + di = value->u.dimm; monitor_printf(mon, "Memory device [%s]: \"%s\"\n", - MemoryDeviceInfoKind_lookup[value->kind], + MemoryDeviceInfoKind_lookup[value->type], di->id ? di->id : ""); monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr); monitor_printf(mon, " slot: %" PRId64 "\n", di->slot); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 80f424b442..d5cdab2707 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -179,7 +179,7 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) NULL); di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); - info->dimm = di; + info->u.dimm = di; elem->value = info; elem->next = NULL; **prev = elem; @@ -203,9 +203,9 @@ ram_addr_t get_current_ram_size(void) MemoryDeviceInfo *value = info->value; if (value) { - switch (value->kind) { + switch (value->type) { case MEMORY_DEVICE_INFO_KIND_DIMM: - size += value->dimm->size; + size += value->u.dimm->size; break; default: break; diff --git a/numa.c b/numa.c index e9b18f54be..fdfe2949d6 100644 --- a/numa.c +++ b/numa.c @@ -226,9 +226,9 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) goto error; } - switch (object->kind) { + switch (object->type) { case NUMA_OPTIONS_KIND_NODE: - numa_node_parse(object->node, opts, &err); + numa_node_parse(object->u.node, opts, &err); if (err) { goto error; } @@ -487,9 +487,9 @@ static void numa_stat_memory_devices(uint64_t node_mem[]) MemoryDeviceInfo *value = info->value; if (value) { - switch (value->kind) { + switch (value->type) { case MEMORY_DEVICE_INFO_KIND_DIMM: - node_mem[value->dimm->node] += value->dimm->size; + node_mem[value->u.dimm->node] += value->u.dimm->size; break; default: break; From ce21131a0b9e556bb73bf65eacdc07ccb21f78a9 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:35:00 -0600 Subject: [PATCH 22/25] tpm: Convert to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. Make the conversion to the new layout for TPM-related code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-22-git-send-email-eblake@redhat.com> [Commit message tweaked slightly] Signed-off-by: Markus Armbruster --- hmp.c | 6 +++--- tpm.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hmp.c b/hmp.c index 39d5815697..a15d00c18c 100644 --- a/hmp.c +++ b/hmp.c @@ -841,11 +841,11 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) c, TpmModel_lookup[ti->model]); monitor_printf(mon, " \\ %s: type=%s", - ti->id, TpmTypeOptionsKind_lookup[ti->options->kind]); + ti->id, TpmTypeOptionsKind_lookup[ti->options->type]); - switch (ti->options->kind) { + switch (ti->options->type) { case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH: - tpo = ti->options->passthrough; + tpo = ti->options->u.passthrough; monitor_printf(mon, "%s%s%s%s", tpo->has_path ? ",path=" : "", tpo->has_path ? tpo->path : "", diff --git a/tpm.c b/tpm.c index 4e9b109fba..f2c59d1f71 100644 --- a/tpm.c +++ b/tpm.c @@ -260,9 +260,9 @@ static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) switch (drv->ops->type) { case TPM_TYPE_PASSTHROUGH: - res->options->kind = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; + res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; tpo = g_new0(TPMPassthroughOptions, 1); - res->options->passthrough = tpo; + res->options->u.passthrough = tpo; if (drv->path) { tpo->path = g_strdup(drv->path); tpo->has_path = true; From e4ba22b31943ab02373359555bd7bcd66442632f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:35:01 -0600 Subject: [PATCH 23/25] qapi: Finish converting to new qapi union layout We have two issues with our qapi union layout: 1) Even though the QMP wire format spells the tag 'type', the C code spells it 'kind', requiring some hacks in the generator. 2) The C struct uses an anonymous union, which places all tag values in the same namespace as all non-variant members. This leads to spurious collisions if a tag value matches a non-variant member's name. This patch is the back end for a series that converts to a saner qapi union layout. Now that all clients have been converted to use 'type' and 'obj->u.value', we can drop the temporary parallel support for 'kind' and 'obj->value'. Given a simple union qapi type: { 'union':'Foo', 'data': { 'a':'int', 'b':'bool' } } this is the overall effect, when compared to the state before this series of patches: | struct Foo { |- FooKind kind; |- union { /* union tag is @kind */ |+ FooKind type; |+ union { /* union tag is @type */ | void *data; | int64_t a; | bool b; |- }; |+ } u; | }; The testsuite still contains some examples of artificial restrictions (see flat-union-clash-type.json, for example) that are no longer technically necessary, now that there is no longer a collision between enum tag values and non-variant member names; but fixing this will be done in later patches, in part because some further changes are required to keep QAPISchema*.check() from asserting. Also, a later patch will add a reservation for the member name 'u' to avoid a collision between a user's non-variant names and our internal choice of C union name. Note, however, that we do not rename the generated enum, which is still 'FooKind'. A further patch could generate implicit enums as 'FooType', but while the generator already reserved the '*Kind' namespace (commit 4dc2e69), there are already QMP constructs with '*Type' naming, which means changing our reservation namespace would have lots of churn to C code to deal with a forced name change. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-23-git-send-email-eblake@redhat.com> [Commit message tweaked] Signed-off-by: Markus Armbruster --- scripts/qapi-types.py | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 1420e00ffb..7e35bb6ac7 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -149,23 +149,10 @@ struct %(c_name)s { if base: ret += gen_struct_fields([], base) else: - # TODO As a hack, we emit both 'kind' and 'type'. Ultimately, we - # want to use only 'type', but the conversion is large enough to - # require staging over several commits. - ret += mcgen(''' - union { - %(c_type)s kind; - %(c_type)s type; - }; -''', - c_type=c_name(variants.tag_member.type.name)) + ret += gen_struct_field(variants.tag_member.name, + variants.tag_member.type, + False) - # TODO As a hack, we emit the union twice, once as an anonymous union - # and once as a named union. Ultimately, we want to use only the - # named union version (as it avoids conflicts between tag values as - # branch names competing with non-variant QMP names), but the conversion - # is large enough to require staging over several commits. - tmp = '' # 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 @@ -174,7 +161,7 @@ struct %(c_name)s { # should not be any data leaks even without a data pointer. Or, if # 'data' is merely added to guarantee we don't have an empty union, # shouldn't we enforce that at .json parse time? - tmp += mcgen(''' + ret += mcgen(''' union { /* union tag is @%(c_name)s */ void *data; ''', @@ -183,17 +170,14 @@ struct %(c_name)s { for var in variants.variants: # Ugly special case for simple union TODO get rid of it typ = var.simple_union_type() or var.type - tmp += mcgen(''' + ret += mcgen(''' %(c_type)s %(c_name)s; ''', c_type=typ.c_type(), c_name=c_name(var.name)) - ret += tmp - ret += ' ' + '\n '.join(tmp.split('\n')) ret += mcgen(''' } u; - }; }; ''') From 5e59baf90a72cd25d38a3134edc029f4f022da74 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:35:02 -0600 Subject: [PATCH 24/25] qapi: Reserve 'u' member name Now that we have separated union tag values from colliding with non-variant C names, by naming the union 'u', we should reserve this name for our use. Note that we want to forbid 'u' even in a struct with no variants, because it is possible for a future qemu release to extend QMP in a backwards-compatible manner while converting from a struct to a flat union. Fortunately, no existing clients were using this member name. If we ever find the need for QMP to have a member 'u', we could at that time relax things, perhaps by having c_name() munge the QMP member to 'q_u'. Note that we cannot forbid 'u' everywhere (by adding the rejection code to check_name()), because the existing QKeyCode enum already uses it; therefore we only reserve it as a struct type member name. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-24-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi.py | 2 +- tests/Makefile | 1 + tests/qapi-schema/reserved-member-u.err | 1 + tests/qapi-schema/reserved-member-u.exit | 1 + tests/qapi-schema/reserved-member-u.json | 7 +++++++ tests/qapi-schema/reserved-member-u.out | 0 6 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/qapi-schema/reserved-member-u.err create mode 100644 tests/qapi-schema/reserved-member-u.exit create mode 100644 tests/qapi-schema/reserved-member-u.json create mode 100644 tests/qapi-schema/reserved-member-u.out diff --git a/scripts/qapi.py b/scripts/qapi.py index 00a16203df..7c50cc4c87 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -490,7 +490,7 @@ def check_type(expr_info, source, value, allow_array=False, for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) - if c_name(key, False).startswith('has_'): + if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): raise QAPIExprError(expr_info, "Member of %s uses reserved name '%s'" % (source, key)) diff --git a/tests/Makefile b/tests/Makefile index fd4ec03e09..92969e8288 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -318,6 +318,7 @@ qapi-schema += redefined-type.json qapi-schema += reserved-command-q.json qapi-schema += reserved-member-has.json qapi-schema += reserved-member-q.json +qapi-schema += reserved-member-u.json qapi-schema += reserved-type-kind.json qapi-schema += reserved-type-list.json qapi-schema += returns-alternate.json diff --git a/tests/qapi-schema/reserved-member-u.err b/tests/qapi-schema/reserved-member-u.err new file mode 100644 index 0000000000..87d42296cc --- /dev/null +++ b/tests/qapi-schema/reserved-member-u.err @@ -0,0 +1 @@ +tests/qapi-schema/reserved-member-u.json:7: Member of 'data' for struct 'Oops' uses reserved name 'u' diff --git a/tests/qapi-schema/reserved-member-u.exit b/tests/qapi-schema/reserved-member-u.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/reserved-member-u.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/reserved-member-u.json b/tests/qapi-schema/reserved-member-u.json new file mode 100644 index 0000000000..1eaf0f301c --- /dev/null +++ b/tests/qapi-schema/reserved-member-u.json @@ -0,0 +1,7 @@ +# Potential C member name collision +# We reject use of 'u' as a member name, to allow it for internal use in +# putting union branch members in a separate namespace from QMP members. +# This is true even for non-unions, because it is possible to convert a +# struct to flat union while remaining backwards compatible in QMP. +# TODO - we could munge the member name to 'q_u' to avoid the collision +{ 'struct': 'Oops', 'data': { 'u': 'str' } } diff --git a/tests/qapi-schema/reserved-member-u.out b/tests/qapi-schema/reserved-member-u.out new file mode 100644 index 0000000000..e69de29bb2 From 32bc6879beea0b0cac6196cb15a71d206401e96d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 26 Oct 2015 16:35:03 -0600 Subject: [PATCH 25/25] qapi: Simplify gen_struct_field() Rather than having all callers pass a name, type, and optional flag, have them instead pass a QAPISchemaObjectTypeMember which already has all that information. No change to generated code. Signed-off-by: Eric Blake Message-Id: <1445898903-12082-25-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi-types.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 7e35bb6ac7..b37900f6fc 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -36,18 +36,18 @@ struct %(c_name)s { c_name=c_name(name), c_type=element_type.c_type()) -def gen_struct_field(name, typ, optional): +def gen_struct_field(member): ret = '' - if optional: + if member.optional: ret += mcgen(''' bool has_%(c_name)s; ''', - c_name=c_name(name)) + c_name=c_name(member.name)) ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=typ.c_type(), c_name=c_name(name)) + c_type=member.type.c_type(), c_name=c_name(member.name)) return ret @@ -60,13 +60,13 @@ def gen_struct_fields(local_members, base=None): ''', c_name=base.c_name()) for memb in base.members: - ret += gen_struct_field(memb.name, memb.type, memb.optional) + ret += gen_struct_field(memb) ret += mcgen(''' /* Own members: */ ''') for memb in local_members: - ret += gen_struct_field(memb.name, memb.type, memb.optional) + ret += gen_struct_field(memb) return ret @@ -149,9 +149,7 @@ struct %(c_name)s { if base: ret += gen_struct_fields([], base) else: - ret += gen_struct_field(variants.tag_member.name, - variants.tag_member.type, - False) + ret += gen_struct_field(variants.tag_member) # 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