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: Markus Armbruster
Subject: Re: [Qemu-devel] [PATCH v2 15/54] qapi: add 'if' to top-level expressions
Date: Mon, 04 Sep 2017 15:27:12 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux)

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?

> 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.

> +
> +
>  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.

> @@ -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.

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



reply via email to

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