qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2 15/54] qapi: add 'if' to top-level expression


From: Marc-André Lureau
Subject: Re: [Qemu-devel] [PATCH v2 15/54] qapi: add 'if' to top-level expressions
Date: Tue, 5 Sep 2017 15:58:12 +0200

Hi

On Mon, Sep 4, 2017 at 3:27 PM, Markus Armbruster <address@hidden> wrote:
> Marc-André Lureau <address@hidden> writes:
>
>> Accept 'if' key in top-level elements, accepted as string or list of
>> string type. The following patches will modify the test visitor to
>> check the value is correctly saved, and generate #if/#endif code (as a
>> single #if/endif line or a series for a list).
>>
>> Example of 'if' key:
>> { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },                        
>>                                                                              
>>                                                           'if': 
>> 'defined(TEST_IF_STRUCT)' }
>
> Lost line break?

yes

>
>> A following patch for qapi-code-gen.txt will provide more complete
>> documentation for 'if' usage.
>>
>> Signed-off-by: Marc-André Lureau <address@hidden>
>> ---
>>  scripts/qapi.py                         | 13 +++++++++++++
>>  tests/test-qmp-commands.c               |  6 ++++++
>>  tests/qapi-schema/qapi-schema-test.json | 20 ++++++++++++++++++++
>>  tests/qapi-schema/qapi-schema-test.out  | 22 ++++++++++++++++++++++
>>  4 files changed, 61 insertions(+)
>>
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index 73adb90379..a94d93ada4 100644
>> --- a/scripts/qapi.py
>> +++ b/scripts/qapi.py
>> @@ -639,6 +639,16 @@ def add_name(name, info, meta, implicit=False):
>>      all_names[name] = meta
>>
>>
>> +def check_if(expr, info):
>> +    ifcond = expr.get('if')
>> +    if not ifcond or isinstance(ifcond, str):
>> +        return
>> +    if (not isinstance(ifcond, list) or
>> +            any([not isinstance(elt, str) for elt in ifcond])):
>> +        raise QAPISemError(info, "'if' condition requires a string or "
>> +                           "a list of string")
>
> The error also triggers on inputs 'if': '' and 'if': [].
>
> The first one doesn't make sense (the C compiler will surely barf).  We
> could leave rejecting it to the C compiler.  If we choose to reject it
> here, we need a better error message, because the one above is
> misleading.
>
> The second one is a roundabout way to say "unconditional".  If we choose
> to reject that, we also need a non-misleading error message.
>
> The error can't trigger on absent 'if', because we don't get called
> then.  To make that locally obvious, please say
>
>        ifcond = expr['if']
>
> Moreover, I'd prefer to avoid mixing conditionals with opposite sense,
> i.e. if good: return; if bad: raise.
>
> Taken together:
>
>    def check_if(expr, info):
>        ifcond = expr['if']
>        if isinstance(ifcond, str):
>            if ifcond == '':
>                raise QAPISemError(info, "'if' condition '' makes no sense")
>            return
>        if isinstance(ifcond, list):
>            if ifcond == []:
>                raise QAPISemError(info, "'if' condition [] is useless")
>            for elt in ifcond:
>                if not isinstance(elt, str):
>                    raise QAPISemError(
>                            info, "'if' condition %s makes no sense" % elt)
>            return
>        raise QAPISemError(
>                info, "'if' condition must be a string or a list of strings")
>
> Written this way (one case after the other), each error has to report
> just one narrow problem.  Makes crafting precise error messages easier.
>

ok, thanks

>> +
>> +
>>  def check_type(info, source, value, allow_array=False,
>>                 allow_dict=False, allow_optional=False,
>>                 allow_metas=[]):
>> @@ -865,6 +875,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>      expr = expr_elem['expr']
>>      info = expr_elem['info']
>>      name = expr[meta]
>> +    optional = optional + ['if']  # all top-level elem accept if
>>      if not isinstance(name, str):
>>          raise QAPISemError(info, "'%s' key must have a string value" % meta)
>>      required = required + [meta]
>
> All top-level expressions require 'data'.  Done the obvious way: all
> callers pass 'data' in @required.  Let's do 'if' the same way for
> consistency.

Not all, 'data' is not required for commands & events.

And 'if' is always optional.

But anyway, I modified it now to pass it as argument...

>
>> @@ -880,6 +891,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
>>              raise QAPISemError(info,
>>                                 "'%s' of %s '%s' should only use true value"
>>                                 % (key, meta, name))
>> +        if key == 'if':
>> +            check_if(expr, info)
>>      for key in required:
>>          if key not in expr:
>>              raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
>> diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
>> index 904c89d4d4..9b9a7ffee7 100644
>> --- a/tests/test-qmp-commands.c
>> +++ b/tests/test-qmp-commands.c
>> @@ -10,6 +10,12 @@
>>
>>  static QmpCommandList qmp_commands;
>>
>> +/* #if defined(TEST_IF_CMD) */
>> +void qmp_TestIfCmd(TestIfStruct *foo, Error **errp)
>> +{
>> +}
>> +/* #endif */
>> +
>>  void qmp_user_def_cmd(Error **errp)
>>  {
>>  }
>> diff --git a/tests/qapi-schema/qapi-schema-test.json 
>> b/tests/qapi-schema/qapi-schema-test.json
>> index c72dbd8050..dc2c444fc1 100644
>> --- a/tests/qapi-schema/qapi-schema-test.json
>> +++ b/tests/qapi-schema/qapi-schema-test.json
>> @@ -188,3 +188,23 @@
>>    'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
>>              'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
>>    'returns': '__org.qemu_x-Union1' }
>> +
>> +# test 'if' condition handling
>> +
>> +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
>> +  'if': 'defined(TEST_IF_STRUCT)' }
>> +
>> +{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
>> +  'if': 'defined(TEST_IF_ENUM)' }
>> +
>> +{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
>> +  'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
>> +
>> +{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 
>> 'TestStruct' },
>> +  'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
>> +
>> +{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
>> +  'if': 'defined(TEST_IF_CMD) && defined(TEST_IF_STRUCT)' }
>> +
>> +{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
>> +  'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
>> diff --git a/tests/qapi-schema/qapi-schema-test.out 
>> b/tests/qapi-schema/qapi-schema-test.out
>> index 3b1e9082d3..7fbaea19bc 100644
>> --- a/tests/qapi-schema/qapi-schema-test.out
>> +++ b/tests/qapi-schema/qapi-schema-test.out
>> @@ -52,6 +52,22 @@ enum QEnumTwo ['value1', 'value2']
>>      prefix QENUM_TWO
>>  enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
>>      prefix QTYPE
>> +alternate TestIfAlternate
>> +    tag type
>> +    case foo: int
>> +    case bar: TestStruct
>> +command TestIfCmd q_obj_TestIfCmd-arg -> None
>> +   gen=True success_response=True boxed=False
>> +enum TestIfEnum ['foo', 'bar']
>> +event TestIfEvent q_obj_TestIfEvent-arg
>> +   boxed=False
>> +object TestIfStruct
>> +    member foo: int optional=False
>> +object TestIfUnion
>> +    member type: TestIfUnionKind optional=False
>> +    tag type
>> +    case foo: q_obj_TestStruct-wrapper
>> +enum TestIfUnionKind ['foo']
>>  object TestStruct
>>      member integer: int optional=False
>>      member boolean: bool optional=False
>> @@ -172,6 +188,12 @@ object q_obj_EVENT_D-arg
>>      member b: str optional=False
>>      member c: str optional=True
>>      member enum3: EnumOne optional=True
>> +object q_obj_TestIfCmd-arg
>> +    member foo: TestIfStruct optional=False
>> +object q_obj_TestIfEvent-arg
>> +    member foo: TestIfStruct optional=False
>> +object q_obj_TestStruct-wrapper
>> +    member data: TestStruct optional=False
>>  object q_obj_UserDefFlatUnion2-base
>>      member integer: int optional=True
>>      member string: str optional=False
>
> The conditionals aren't visible in qapi-schema-test.out.  They should
> be.
>

That's a follow-up patch "qapi: add 'ifcond' to visitor methods"

> *Much* easier to review than its predecessor PATCH 07/26.  Appreciated!



-- 
Marc-André Lureau



reply via email to

[Prev in Thread] Current Thread [Next in Thread]