mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00
qapi: Require boxed for conditional command and event arguments
The C code generator fails to honor 'if' conditions of command and event arguments. For instance, tests/qapi-schema/qapi-schema-test.json has { 'event': 'TEST_IF_EVENT', 'data': { 'foo': 'TestIfStruct', 'bar': { 'type': ['str'], 'if': 'TEST_IF_EVT_ARG' } }, 'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } } Generated tests/test-qapi-events.h fails to honor the TEST_IF_EVT_ARG condition: #if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) void qapi_event_send_test_if_event(TestIfStruct *foo, strList *bar); #endif /* defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) */ Only uses so far are in tests/. We could fix the generator to emit something like #if defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) void qapi_event_send_test_if_event(TestIfStruct *foo #if defined(TEST_IF_EVT_ARG) , strList *bar #endif ); #endif /* defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT) */ Ugly. Calls become similarly ugly. Not worth fixing. Conditional arguments work fine with 'boxed': true, simply because complex types with conditional members work fine. Not worth breaking. Reject conditional arguments unless boxed. Move the tests cases covering unboxed conditional arguments out of tests/qapi-schema/qapi-schema-test.json. Cover boxed conditional arguments there instead. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20230316071325.492471-15-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
713d921aed
commit
de3b3f529d
16 changed files with 48 additions and 22 deletions
|
@ -805,9 +805,8 @@ gets its generated code guarded like this::
|
||||||
... generated code ...
|
... generated code ...
|
||||||
#endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */
|
#endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */
|
||||||
|
|
||||||
Individual members of complex types, commands arguments, and
|
Individual members of complex types can also be made conditional.
|
||||||
event-specific data can also be made conditional. This requires the
|
This requires the longhand form of MEMBER.
|
||||||
longhand form of MEMBER.
|
|
||||||
|
|
||||||
Example: a struct type with unconditional member 'foo' and conditional
|
Example: a struct type with unconditional member 'foo' and conditional
|
||||||
member 'bar' ::
|
member 'bar' ::
|
||||||
|
|
|
@ -66,6 +66,7 @@ def gen_call(name: str,
|
||||||
elif arg_type:
|
elif arg_type:
|
||||||
assert not arg_type.variants
|
assert not arg_type.variants
|
||||||
for memb in arg_type.members:
|
for memb in arg_type.members:
|
||||||
|
assert not memb.ifcond.is_present()
|
||||||
if memb.need_has():
|
if memb.need_has():
|
||||||
argstr += 'arg.has_%s, ' % c_name(memb.name)
|
argstr += 'arg.has_%s, ' % c_name(memb.name)
|
||||||
argstr += 'arg.%s, ' % c_name(memb.name)
|
argstr += 'arg.%s, ' % c_name(memb.name)
|
||||||
|
|
|
@ -119,6 +119,7 @@ def build_params(arg_type: Optional[QAPISchemaObjectType],
|
||||||
elif arg_type:
|
elif arg_type:
|
||||||
assert not arg_type.variants
|
assert not arg_type.variants
|
||||||
for memb in arg_type.members:
|
for memb in arg_type.members:
|
||||||
|
assert not memb.ifcond.is_present()
|
||||||
ret += sep
|
ret += sep
|
||||||
sep = ', '
|
sep = ', '
|
||||||
if memb.need_has():
|
if memb.need_has():
|
||||||
|
|
|
@ -486,6 +486,10 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||||
assert self.members is not None
|
assert self.members is not None
|
||||||
return not self.members and not self.variants
|
return not self.members and not self.variants
|
||||||
|
|
||||||
|
def has_conditional_members(self):
|
||||||
|
assert self.members is not None
|
||||||
|
return any(m.ifcond.is_present() for m in self.members)
|
||||||
|
|
||||||
def c_name(self):
|
def c_name(self):
|
||||||
assert self.name != 'q_empty'
|
assert self.name != 'q_empty'
|
||||||
return super().c_name()
|
return super().c_name()
|
||||||
|
@ -817,6 +821,11 @@ class QAPISchemaCommand(QAPISchemaEntity):
|
||||||
self.info,
|
self.info,
|
||||||
"command's 'data' can take %s only with 'boxed': true"
|
"command's 'data' can take %s only with 'boxed': true"
|
||||||
% self.arg_type.describe())
|
% self.arg_type.describe())
|
||||||
|
self.arg_type.check(schema)
|
||||||
|
if self.arg_type.has_conditional_members() and not self.boxed:
|
||||||
|
raise QAPISemError(
|
||||||
|
self.info,
|
||||||
|
"conditional command arguments require 'boxed': true")
|
||||||
if self._ret_type_name:
|
if self._ret_type_name:
|
||||||
self.ret_type = schema.resolve_type(
|
self.ret_type = schema.resolve_type(
|
||||||
self._ret_type_name, self.info, "command's 'returns'")
|
self._ret_type_name, self.info, "command's 'returns'")
|
||||||
|
@ -872,6 +881,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
|
||||||
self.info,
|
self.info,
|
||||||
"event's 'data' can take %s only with 'boxed': true"
|
"event's 'data' can take %s only with 'boxed': true"
|
||||||
% self.arg_type.describe())
|
% self.arg_type.describe())
|
||||||
|
self.arg_type.check(schema)
|
||||||
|
if self.arg_type.has_conditional_members() and not self.boxed:
|
||||||
|
raise QAPISemError(
|
||||||
|
self.info,
|
||||||
|
"conditional event arguments require 'boxed': true")
|
||||||
|
|
||||||
def connect_doc(self, doc=None):
|
def connect_doc(self, doc=None):
|
||||||
super().connect_doc(doc)
|
super().connect_doc(doc)
|
||||||
|
|
2
tests/qapi-schema/args-if-implicit.err
Normal file
2
tests/qapi-schema/args-if-implicit.err
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
args-if-implicit.json: In command 'test-if-cmd':
|
||||||
|
args-if-implicit.json:1: conditional command arguments require 'boxed': true
|
4
tests/qapi-schema/args-if-implicit.json
Normal file
4
tests/qapi-schema/args-if-implicit.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{ 'command': 'test-if-cmd',
|
||||||
|
'data': {
|
||||||
|
'foo': 'int',
|
||||||
|
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } }
|
0
tests/qapi-schema/args-if-implicit.out
Normal file
0
tests/qapi-schema/args-if-implicit.out
Normal file
2
tests/qapi-schema/args-if-unboxed.err
Normal file
2
tests/qapi-schema/args-if-unboxed.err
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
args-if-unboxed.json: In command 'test-if-cmd':
|
||||||
|
args-if-unboxed.json:5: conditional command arguments require 'boxed': true
|
6
tests/qapi-schema/args-if-unboxed.json
Normal file
6
tests/qapi-schema/args-if-unboxed.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{ 'struct': 'TestIfCmdArgs',
|
||||||
|
'data': {
|
||||||
|
'foo': 'int',
|
||||||
|
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } }
|
||||||
|
{ 'command': 'test-if-cmd',
|
||||||
|
'data': 'TestIfCmdArgs' }
|
0
tests/qapi-schema/args-if-unboxed.out
Normal file
0
tests/qapi-schema/args-if-unboxed.out
Normal file
2
tests/qapi-schema/event-args-if-unboxed.err
Normal file
2
tests/qapi-schema/event-args-if-unboxed.err
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
tests/qapi-schema/event-args-if-unboxed.json: In event 'TEST_IF_EVENT':
|
||||||
|
tests/qapi-schema/event-args-if-unboxed.json:1: event's 'data' members may have 'if' conditions only with 'boxed': true
|
4
tests/qapi-schema/event-args-if-unboxed.json
Normal file
4
tests/qapi-schema/event-args-if-unboxed.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{ 'event': 'TEST_IF_EVENT',
|
||||||
|
'data': {
|
||||||
|
'foo': 'int',
|
||||||
|
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } } }
|
0
tests/qapi-schema/event-args-if-unboxed.out
Normal file
0
tests/qapi-schema/event-args-if-unboxed.out
Normal file
|
@ -27,6 +27,8 @@ schemas = [
|
||||||
'args-bad-boxed.json',
|
'args-bad-boxed.json',
|
||||||
'args-boxed-anon.json',
|
'args-boxed-anon.json',
|
||||||
'args-boxed-string.json',
|
'args-boxed-string.json',
|
||||||
|
'args-if-implicit.json',
|
||||||
|
'args-if-unboxed.json',
|
||||||
'args-int.json',
|
'args-int.json',
|
||||||
'args-invalid.json',
|
'args-invalid.json',
|
||||||
'args-member-array-bad.json',
|
'args-member-array-bad.json',
|
||||||
|
|
|
@ -249,17 +249,16 @@
|
||||||
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'command': 'test-if-cmd',
|
{ 'command': 'test-if-cmd',
|
||||||
'data': {
|
'boxed': true,
|
||||||
'foo': 'TestIfStruct',
|
'data': 'TestIfStruct',
|
||||||
'bar': { 'type': 'str', 'if': 'TEST_IF_CMD_ARG' } },
|
|
||||||
'returns': 'UserDefThree',
|
'returns': 'UserDefThree',
|
||||||
'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' }
|
{ 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' }
|
||||||
|
|
||||||
{ 'event': 'TEST_IF_EVENT',
|
{ 'event': 'TEST_IF_EVENT',
|
||||||
'data': { 'foo': 'TestIfStruct',
|
'boxed': true,
|
||||||
'bar': { 'type': ['str'], 'if': 'TEST_IF_EVT_ARG' } },
|
'data': 'TestIfStruct',
|
||||||
'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
|
'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
|
||||||
|
|
||||||
{ 'event': 'TEST_IF_EVENT2', 'data': {},
|
{ 'event': 'TEST_IF_EVENT2', 'data': {},
|
||||||
|
|
|
@ -283,23 +283,13 @@ object q_obj_test-if-alternate-cmd-arg
|
||||||
command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
|
command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']}
|
||||||
object q_obj_test-if-cmd-arg
|
command test-if-cmd TestIfStruct -> UserDefThree
|
||||||
member foo: TestIfStruct optional=False
|
gen=True success_response=True boxed=True oob=False preconfig=False
|
||||||
member bar: str optional=False
|
|
||||||
if TEST_IF_CMD_ARG
|
|
||||||
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
|
|
||||||
command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree
|
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
|
||||||
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']}
|
||||||
command test-cmd-return-def-three None -> UserDefThree
|
command test-cmd-return-def-three None -> UserDefThree
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
object q_obj_TEST_IF_EVENT-arg
|
event TEST_IF_EVENT TestIfStruct
|
||||||
member foo: TestIfStruct optional=False
|
boxed=True
|
||||||
member bar: strList optional=False
|
|
||||||
if TEST_IF_EVT_ARG
|
|
||||||
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
|
|
||||||
event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg
|
|
||||||
boxed=False
|
|
||||||
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
|
if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']}
|
||||||
event TEST_IF_EVENT2 None
|
event TEST_IF_EVENT2 None
|
||||||
boxed=False
|
boxed=False
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue