qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level sch


From: Marc-André Lureau
Subject: Re: [Qemu-devel] [PATCH 07/26] qapi: add 'if' condition on top-level schema elements
Date: Tue, 22 Aug 2017 13:17:55 +0200

Hi

On Wed, Aug 16, 2017 at 5:43 PM, Markus Armbruster <address@hidden> wrote:
> Marc-André Lureau <address@hidden> writes:
>
>> Add 'if' c-preprocessor condition on top-level schema elements:
>> struct, enum, union, alternate, command, event.
>
> An example would be useful here.  Your cover letter has nice ones, would
> be a shame not to preserve them for posterity in the commit log.
>

ok

>> Variants objects types are created outside of #if blocks, since they
>
> What are "variants objects types"?
>

I think I meant implicit objects types, that may be created by variant.

>> may be shared by various types. Even if unused, this shouldn't be an
>> issue, since those are internal types.
>>
>> Note that there is no checks in qapi scripts to verify that elements
>
> there are
>
>> have compatible conditions (ex: if-struct used by a if-foo
>> command). This may thus fail at C build time if they don't share the
>> same subset of conditions.
>
> Fair enough.
>
>> Signed-off-by: Marc-André Lureau <address@hidden>
>> ---
>>  scripts/qapi.py                         | 153 
>> +++++++++++++++++++++++++-------
>>  scripts/qapi-commands.py                |   4 +-
>>  scripts/qapi-event.py                   |   4 +-
>>  scripts/qapi-introspect.py              |  46 ++++++----
>>  scripts/qapi-types.py                   |  51 +++++++----
>>  scripts/qapi-visit.py                   |  13 ++-
>>  scripts/qapi2texi.py                    |  10 +--
>>  tests/Makefile.include                  |   1 +
>>  tests/qapi-schema/bad-if.err            |   1 +
>>  tests/qapi-schema/bad-if.exit           |   1 +
>>  tests/qapi-schema/bad-if.json           |   3 +
>>  tests/qapi-schema/bad-if.out            |   0
>>  tests/qapi-schema/qapi-schema-test.json |  20 +++++
>>  tests/qapi-schema/qapi-schema-test.out  |  31 +++++++
>>  tests/qapi-schema/test-qapi.py          |  21 +++--
>>  15 files changed, 272 insertions(+), 87 deletions(-)
>>  create mode 100644 tests/qapi-schema/bad-if.err
>>  create mode 100644 tests/qapi-schema/bad-if.exit
>>  create mode 100644 tests/qapi-schema/bad-if.json
>>  create mode 100644 tests/qapi-schema/bad-if.out
>>
>> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> index 4ecc19e944..79ba1e93da 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")
>
> Wait a second!  What's this list business?  The commit message doesn't
> say.

Updated commit message, and documented in docs/devel/qapi-code-gen.txt

>
> Also, pep8 gripes:
>
>     scripts/qapi.py:647:9: E129 visually indented line with same indent as 
> next logical line
>

fixed

>> +
>> +
>>  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.append('if')
>
> Caution!
>
>     $ python
>     Python 2.7.13 (default, May 10 2017, 20:04:36)
>     >>> def surprise(arg=[]):
>     ...     arg.append('if')
>     ...     return arg
>     ...
>     >>> surprise()
>     ['if']
>     >>> surprise()
>     ['if', 'if']
>     >>> surprise()
>     ['if', 'if', 'if']
>
> Never modify an argument that has list or dictionary default value.  To
> avoid the temptation, never use such defaul values.

Oops! Without bad consequences here, but fixed anyway.

>
>>      if not isinstance(name, str):
>>          raise QAPISemError(info, "'%s' key must have a string value" % meta)
>>      required = required + [meta]
>> @@ -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'"
>> @@ -989,6 +1002,10 @@ class QAPISchemaEntity(object):
>>          # such place).
>>          self.info = info
>>          self.doc = doc
>> +        self.ifcond = None
>> +
>> +    def set_ifcond(self, ifcond):
>> +        self.ifcond = ifcond
>
> @ifcond is an awkward name, but I don't have better ideas.

Yeah, I got used to it now ;)

>
>>
>>      def c_name(self):
>>          return c_name(self.name)
>> @@ -1017,26 +1034,26 @@ class QAPISchemaVisitor(object):
>>      def visit_builtin_type(self, name, info, json_type):
>>          pass
>>
>> -    def visit_enum_type(self, name, info, values, prefix):
>> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>>          pass
>>
>> -    def visit_array_type(self, name, info, element_type):
>> +    def visit_array_type(self, name, info, element_type, ifcond):
>>          pass
>>
>> -    def visit_object_type(self, name, info, base, members, variants):
>> +    def visit_object_type(self, name, info, base, members, variants, 
>> ifcond):
>>          pass
>>
>> -    def visit_object_type_flat(self, name, info, members, variants):
>> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>>          pass
>>
>> -    def visit_alternate_type(self, name, info, variants):
>> +    def visit_alternate_type(self, name, info, variants, ifcond):
>>          pass
>>
>>      def visit_command(self, name, info, arg_type, ret_type,
>> -                      gen, success_response, boxed):
>> +                      gen, success_response, boxed, ifcond):
>>          pass
>>
>> -    def visit_event(self, name, info, arg_type, boxed):
>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>          pass
>>
>>
>> @@ -1136,7 +1153,7 @@ class QAPISchemaEnumType(QAPISchemaType):
>>
>>      def visit(self, visitor):
>>          visitor.visit_enum_type(self.name, self.info,
>> -                                self.member_names(), self.prefix)
>> +                                self.member_names(), self.prefix, 
>> self.ifcond)
>>
>>
>>  class QAPISchemaArrayType(QAPISchemaType):
>> @@ -1149,6 +1166,7 @@ class QAPISchemaArrayType(QAPISchemaType):
>>      def check(self, schema):
>>          self.element_type = schema.lookup_type(self._element_type_name)
>>          assert self.element_type
>> +        self.ifcond = self.element_type.ifcond
>>
>>      def is_implicit(self):
>>          return True
>> @@ -1166,7 +1184,8 @@ class QAPISchemaArrayType(QAPISchemaType):
>>          return 'array of ' + elt_doc_type
>>
>>      def visit(self, visitor):
>> -        visitor.visit_array_type(self.name, self.info, self.element_type)
>> +        visitor.visit_array_type(self.name, self.info, self.element_type,
>> +                                 self.ifcond)
>>
>>
>>  class QAPISchemaObjectType(QAPISchemaType):
>> @@ -1247,9 +1266,10 @@ class QAPISchemaObjectType(QAPISchemaType):
>>
>>      def visit(self, visitor):
>>          visitor.visit_object_type(self.name, self.info,
>> -                                  self.base, self.local_members, 
>> self.variants)
>> +                                  self.base, self.local_members, 
>> self.variants,
>> +                                  self.ifcond)
>>          visitor.visit_object_type_flat(self.name, self.info,
>> -                                       self.members, self.variants)
>> +                                       self.members, self.variants, 
>> self.ifcond)
>
> You break the line before self.ifcond almost everywhere, and when you
> don't, the line gets long.  This one goes over the limit:
>
>     scripts/qapi.py:1285:80: E501 line too long (80 > 79 characters)
>
> Suggest to break it before self.ifcond everywhere.
>

ok

>>
>>
>>  class QAPISchemaMember(object):
>> @@ -1392,7 +1412,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
>>          return 'value'
>>
>>      def visit(self, visitor):
>> -        visitor.visit_alternate_type(self.name, self.info, self.variants)
>> +        visitor.visit_alternate_type(self.name, self.info,
>> +                                     self.variants, self.ifcond)
>>
>>      def is_empty(self):
>>          return False
>> @@ -1434,7 +1455,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
>>      def visit(self, visitor):
>>          visitor.visit_command(self.name, self.info,
>>                                self.arg_type, self.ret_type,
>> -                              self.gen, self.success_response, self.boxed)
>> +                              self.gen, self.success_response, self.boxed,
>> +                              self.ifcond)
>>
>>
>>  class QAPISchemaEvent(QAPISchemaEntity):
>> @@ -1462,7 +1484,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
>>              raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
>>
>>      def visit(self, visitor):
>> -        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
>> +        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed,
>> +                            self.ifcond)
>>
>>
>>  class QAPISchema(object):
>> @@ -1481,11 +1504,12 @@ class QAPISchema(object):
>>              print >>sys.stderr, err
>>              exit(1)
>>
>> -    def _def_entity(self, ent):
>> +    def _def_entity(self, ent, ifcond=None):
>>          # Only the predefined types are allowed to not have info
>>          assert ent.info or self._predefining
>>          assert ent.name not in self._entity_dict
>>          self._entity_dict[ent.name] = ent
>> +        ent.set_ifcond(ifcond)
>
> .set_ifcond(None) does the right thing.
>
> However:
>
>>
>>      def lookup_entity(self, name, typ=None):
>>          ent = self._entity_dict.get(name)
>> @@ -1534,11 +1558,11 @@ class QAPISchema(object):
>>      def _make_enum_members(self, values):
>>          return [QAPISchemaMember(v) for v in values]
>>
>> -    def _make_implicit_enum_type(self, name, info, values):
>> +    def _make_implicit_enum_type(self, name, info, values, ifcond):
>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>          name = name + 'Kind'   # Use namespace reserved by add_name()
>>          self._def_entity(QAPISchemaEnumType(
>> -            name, info, None, self._make_enum_members(values), None))
>> +            name, info, None, self._make_enum_members(values), None), 
>> ifcond)
>>          return name
>
> Why is ifcond not a constructor argument like name, info, and so forth?
> What makes it special?
>

I think it was easier that way (builder pattern), but it make sense as
constructor too. Changed

>>
>>      def _make_array_type(self, element_type, info):
>> @@ -1547,22 +1571,26 @@ class QAPISchema(object):
>>              self._def_entity(QAPISchemaArrayType(name, info, element_type))
>>          return name
>>
>> -    def _make_implicit_object_type(self, name, info, doc, role, members):
>> +    def _make_implicit_object_type(self, name, info, doc, role, members,
>> +                                   ifcond=None):
>>          if not members:
>>              return None
>>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>>          name = 'q_obj_%s-%s' % (name, role)
>> -        if not self.lookup_entity(name, QAPISchemaObjectType):
>> +        if self.lookup_entity(name, QAPISchemaObjectType):
>> +            assert ifcond is None
>> +        else:
>>              self._def_entity(QAPISchemaObjectType(name, info, doc, None,
>> -                                                  members, None))
>> +                                                  members, None), ifcond)
>>          return name
>
> Hmm, this smells like it might be the "variants objects types" mentioned
> in the commit message.
>
> Types made with _make_implicit_object_type():
>
> * The wrapper around "simple" union members, by _make_simple_variant()
>
> * A flat union's base type when it's implicit, by _def_union_type()
>
> * A command's argument type when it's implicit, by _def_command()
>
> * An event's argument type when it's implicit, by _def_event()
>
> Only the first one can be used more than once, namely when a type occurs
> in more than one simple union.  The "correct" ifcond is the disjunction
> of all its user's ifconds.  You make it use the *first* ifcond instead.
> Wrong: breaks when one of the other simple unions has a condition that
> is true when the first one's is false.
>
> Your commit message suggests you intended to make it unconditional
> instead.  That would work: the worst that can happen is compiling a few
> q_obj_FOO_wrapper typedefs and visit_type_q_FOO_wrapper() functions that
> aren't actually used.  Tolerable, in particular since I hope to get rid
> of "simple" unions some day.
>
> Sadly, it would prevent us from making the visit functions for implicit
> types static, because unused static functions trigger warnings.  Let's
> not worry about that now.
>
> Generating the disjunction of all conditions wouldn't be terribly hard.
> I'm not asking for it, though.

I suggest to tackle it as follow-up then. Added a FIXME


>
> You assert that implicit types are unconditional from the second use on.
> I guess you mean to assert the ones used more than once are
> unconditional:
>
>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>            if typ:
>                assert ifcond is None and typ.ifcond is None
>
> But what you *should* assert is that the conditions are the same:
>
>            typ = self.lookup_entity(name, QAPISchemaObjectType)
>            if typ:
>                assert ifcond == typ.ifcond
>
>>

ok

>>      def _def_enum_type(self, expr, info, doc):
>>          name = expr['enum']
>>          data = expr['data']
>>          prefix = expr.get('prefix')
>> -        self._def_entity(QAPISchemaEnumType(
>> -            name, info, doc, self._make_enum_members(data), prefix))
>> +        return self._def_entity(QAPISchemaEnumType(
>> +            name, info, doc, self._make_enum_members(data), prefix),
>> +                                expr.get('if'))
>
> Covers enumeration types.
>
> Why return?  The (only) caller throws the value away...

left-over, fixed

>
>>
>>      def _make_member(self, name, typ, info):
>>          optional = False
>> @@ -1584,7 +1612,8 @@ class QAPISchema(object):
>>          data = expr['data']
>>          self._def_entity(QAPISchemaObjectType(name, info, doc, base,
>>                                                self._make_members(data, 
>> info),
>> -                                              None))
>> +                                              None),
>> +                         expr.get('if'))
>
> Covers struct types.
>
>>
>>      def _make_variant(self, case, typ):
>>          return QAPISchemaObjectTypeVariant(case, typ)
>> @@ -1593,8 +1622,10 @@ class QAPISchema(object):
>>          if isinstance(typ, list):
>>              assert len(typ) == 1
>>              typ = self._make_array_type(typ[0], info)
>> +        type_entity = self.lookup_type(typ)
>>          typ = self._make_implicit_object_type(
>> -            typ, info, None, 'wrapper', [self._make_member('data', typ, 
>> info)])
>> +            typ, info, None, 'wrapper',
>> +            [self._make_member('data', typ, info)], type_entity.ifcond)
>>          return QAPISchemaObjectTypeVariant(case, typ)
>
> A simple union member's wrapper type inherits its condition from the
> member type.
>
> I think you need to pass None instead of type_entity.ifcond here.

That doesn't work, visitor symbols are missing in generated code. Why
shouldn't the wrapper share the same condition as the underlying type?

>
>>
>>      def _def_union_type(self, expr, info, doc):
>> @@ -1604,8 +1635,9 @@ class QAPISchema(object):
>>          tag_name = expr.get('discriminator')
>>          tag_member = None
>>          if isinstance(base, dict):
>> -            base = (self._make_implicit_object_type(
>> -                name, info, doc, 'base', self._make_members(base, info)))
>> +            base = self._make_implicit_object_type(
>> +                name, info, doc, 'base', self._make_members(base, info),
>> +                expr.get('if'))
>
> A flat union's implicit base type inherits its condition from the flat
> union.  Good.
>
>>          if tag_name:
>>              variants = [self._make_variant(key, value)
>>                          for (key, value) in data.iteritems()]
>> @@ -1614,14 +1646,16 @@ class QAPISchema(object):
>>              variants = [self._make_simple_variant(key, value, info)
>>                          for (key, value) in data.iteritems()]
>>              typ = self._make_implicit_enum_type(name, info,
>> -                                                [v.name for v in variants])
>> +                                                [v.name for v in variants],
>> +                                                expr.get('if'))
>
> A flat union's implicit enumeration type inherits its condition from the
> flat union.  Good.
>
>>              tag_member = QAPISchemaObjectTypeMember('type', typ, False)
>>              members = [tag_member]
>>          self._def_entity(
>>              QAPISchemaObjectType(name, info, doc, base, members,
>>                                   QAPISchemaObjectTypeVariants(tag_name,
>>                                                                tag_member,
>> -                                                              variants)))
>> +                                                              variants)),
>> +            expr.get('if'))
>
> Covers union types.
>
> Third use of expr.get('if') in this function.  Please put it in a
> variable.
>
> Actually, do that for *all* uses of expr[X] and expr.get(X) in class
> QAPISchema, because that's how the existing code works.

done

>
>>
>>      def _def_alternate_type(self, expr, info, doc):
>>          name = expr['alternate']
>> @@ -1633,7 +1667,8 @@ class QAPISchema(object):
>>              QAPISchemaAlternateType(name, info, doc,
>>                                      QAPISchemaObjectTypeVariants(None,
>>                                                                   tag_member,
>> -                                                                 variants)))
>> +                                                                 variants)),
>> +            expr.get('if'))
>
> Covers alternate types.
>
>>
>>      def _def_command(self, expr, info, doc):
>>          name = expr['command']
>> @@ -1644,12 +1679,14 @@ class QAPISchema(object):
>>          boxed = expr.get('boxed', False)
>>          if isinstance(data, OrderedDict):
>>              data = self._make_implicit_object_type(
>> -                name, info, doc, 'arg', self._make_members(data, info))
>> +                name, info, doc, 'arg', self._make_members(data, info),
>> +                expr.get('if'))
>
> A command's implicit argument type inherits its condition from the
> command.  Good.
>
>>          if isinstance(rets, list):
>>              assert len(rets) == 1
>>              rets = self._make_array_type(rets[0], info)
>>          self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
>> -                                           gen, success_response, boxed))
>> +                                           gen, success_response, boxed),
>> +                         expr.get('if'))
>
> Covers commands.
>
>>
>>      def _def_event(self, expr, info, doc):
>>          name = expr['event']
>> @@ -1657,8 +1694,10 @@ class QAPISchema(object):
>>          boxed = expr.get('boxed', False)
>>          if isinstance(data, OrderedDict):
>>              data = self._make_implicit_object_type(
>> -                name, info, doc, 'arg', self._make_members(data, info))
>> -        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
>> +                name, info, doc, 'arg', self._make_members(data, info),
>> +                expr.get('if'))
>
> An event's implicit argument type inherits its condition from the
> command.  Good.
>
>> +        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed),
>> +                         expr.get('if'))
>
> Covers events.  You got them all.  Good.
>
>>
>>      def _def_exprs(self):
>>          for expr_elem in self.exprs:
>> @@ -1848,6 +1887,54 @@ def guardend(name):
>>                   name=guardname(name))
>>
>>
>> +def gen_if(ifcond, func=''):
>> +    if not ifcond:
>> +        return ''
>> +    if isinstance(ifcond, str):
>> +        ifcond = [ifcond]
>> +    ret = '\n'
>> +    for ifc in ifcond:
>> +        ret += mcgen('#if %(ifcond)s /* %(func)s */\n', ifcond=ifc, 
>> func=func)
>> +    ret += '\n'
>> +    return ret
>
> Please use mcgen() like the existing code:
>

mcgen() does indent and fails with pre-processor lines. I added a comment

>            ret += mcgen('''
>    #if %(ifcond)s /* %(func)s */
>    ''', ifcond=ifc, func=func)
>
> With the default value of @func, we get a useless, ugly comment /* */.
> If this can happen, please suppress the comment.  Else, drop @func's
> default value.
>
> Lists appear to be conjunctions.  What for?

I added some doc in qapi-code-gen.txt

>
>> +
>> +
>> +def gen_endif(ifcond, func=''):
>> +    if not ifcond:
>> +        return ''
>> +    if isinstance(ifcond, str):
>> +        ifcond = [ifcond]
>> +    ret = '\n'
>> +    for ifc in reversed(ifcond):
>> +        ret += mcgen('#endif /* %(ifcond)s %(func)s */\n',
>> +                     ifcond=ifc, func=func)
>> +    ret += '\n'
>> +    return ret
>
> Likewise.
>
>> +
>> +
>> +# wrap a method to add #if / #endif to generated code
>> +# self must have 'if_members' listing the attributes to wrap
>> +# the last argument of the wrapped function must be the 'ifcond'
>
> Start your sentences with a capital letter, and end them with a period,
> please.

ok

>
>> +def if_wrap(func):
>
> Blank line, please.

ok

>
>> +    def func_wrapper(self, *args, **kwargs):
>> +        funcname = self.__class__.__name__ + '.' + func.__name__
>> +        ifcond = args[-1]
>> +        save = {}
>> +        for mem in self.if_members:
>> +            save[mem] = getattr(self, mem)
>> +        func(self, *args, **kwargs)
>> +        for mem, val in save.items():
>> +            newval = getattr(self, mem)
>> +            if newval != val:
>> +                assert newval.startswith(val)
>> +                newval = newval[len(val):]
>> +                val += gen_if(ifcond, funcname)
>
> Emitting comments pointing to the QAPI schema into the generated code is
> often helpful.  But this one points to QAPI generator code.  Why is that
> useful?

That was mostly useful during development, removed

>
>> +                val += newval
>> +                val += gen_endif(ifcond, funcname)
>> +            setattr(self, mem, val)
>> +
>> +    return func_wrapper
>> +
>
> pep8 again:
>
>     scripts/qapi.py:1955:1: E302 expected 2 blank lines, found 1
>
> Peeking ahead: this function is used as a decorator.  Let's help the
> reader and mention that in the function comment, or by naming the
> function suitably.  ifcond_decorator?

done

>
> Messing with the wrapped method's class's attributes is naughty.  Worse,
> it's hard to understand.  What alternatives have you considered?

Well, I started writing the code that checked if members got code
added, I had to put some enter()/leave() code everywhere. Then I
realize this could easily be the job for a decorator. I think the end
result is pretty neat. If you have a better idea, can you do it in a
follow-up?

>
>>  def gen_enum_lookup(name, values, prefix=None):
>>      ret = mcgen('''
>>
>> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
>> index 974d0a4a80..19b1bb9b88 100644
>> --- a/scripts/qapi-commands.py
>> +++ b/scripts/qapi-commands.py
>> @@ -228,6 +228,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
>>          self.defn = None
>>          self._regy = None
>>          self._visited_ret_types = None
>> +        self.if_members = ['decl', 'defn', '_regy']
>>
>>      def visit_begin(self, schema):
>>          self.decl = ''
>> @@ -240,8 +241,9 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
>>          self._regy = None
>>          self._visited_ret_types = None
>>
>> +    @if_wrap
>>      def visit_command(self, name, info, arg_type, ret_type,
>> -                      gen, success_response, boxed):
>> +                      gen, success_response, boxed, ifcond):
>>          if not gen:
>>              return
>>          self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
>> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
>> index bcbef1035f..cad9ece790 100644
>> --- a/scripts/qapi-event.py
>> +++ b/scripts/qapi-event.py
>> @@ -152,6 +152,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
>>          self.decl = None
>>          self.defn = None
>>          self._event_names = None
>> +        self.if_members = ['decl', 'defn']
>>
>>      def visit_begin(self, schema):
>>          self.decl = ''
>> @@ -163,7 +164,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
>>          self.defn += gen_enum_lookup(event_enum_name, self._event_names)
>>          self._event_names = None
>>
>> -    def visit_event(self, name, info, arg_type, boxed):
>> +    @if_wrap
>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>          self.decl += gen_event_send_decl(name, arg_type, boxed)
>>          self.defn += gen_event_send(name, arg_type, boxed)
>>          self._event_names.append(name)
>> diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
>> index fc72cdb66d..ecfb0f2752 100644
>> --- a/scripts/qapi-introspect.py
>> +++ b/scripts/qapi-introspect.py
>> @@ -12,7 +12,7 @@
>>  from qapi import *
>>
>>
>> -def to_qlit(obj, level=0, first_indent=True):
>> +def to_qlit(obj, level=0, first_indent=True, suffix=''):
>>      def indent(level):
>>          return level * 4 * ' '
>>      ret = ''
>> @@ -20,14 +20,20 @@ def to_qlit(obj, level=0, first_indent=True):
>>          ret += indent(level)
>>      if obj is None:
>>          ret += 'QLIT_QNULL'
>> +    elif isinstance(obj, tuple):
>> +        obj, ifcond =  obj
>> +        ret += gen_if(ifcond)
>> +        ret += to_qlit(obj, level, False) + suffix
>
> Keyword argument instead of bare False, please.

ok

>
>> +        ret += gen_endif(ifcond)
>> +        suffix = ''
>
> New case tuple, for generating conditionals.  Okay.
>
>>      elif isinstance(obj, str):
>>          ret += 'QLIT_QSTR(' + '"' + obj.replace('"', r'\"') + '"' + ')'
>>      elif isinstance(obj, list):
>> -        elts = [to_qlit(elt, level + 1)
>> +        elts = [to_qlit(elt, level + 1, True, ",")
>
> Make that
>
>            elts = [to_qlit(elt, level + 1, suffix=",")

ok

>
>>                  for elt in obj]
>>          elts.append(indent(level + 1) + "{ }")
>>          ret += 'QLIT_QLIST(((QLitObject[]) {\n'
>> -        ret += ',\n'.join(elts) + '\n'
>> +        ret += '\n'.join(elts) + '\n'
>>          ret += indent(level) + '}))'
>>      elif isinstance(obj, dict):
>>          elts = [ indent(level + 1) + '{ "%s", %s }' %
>> @@ -39,7 +45,7 @@ def to_qlit(obj, level=0, first_indent=True):
>>          ret += indent(level) + '}))'
>>      else:
>>          assert False                # not implemented
>> -    return ret
>> +    return ret + suffix
>
> This is getting really hard to review --- my brain is about to overflow
> and shut down for the day.  Can you split off some preparatory work?
> The introduction of suffix here, perhaps?

ok

>
>>
>>
>>  class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
>> @@ -113,12 +119,12 @@ const QLitObject %(c_name)s = %(c_string)s;
>>              return '[' + self._use_type(typ.element_type) + ']'
>>          return self._name(typ.name)
>>
>> -    def _gen_qlit(self, name, mtype, obj):
>> +    def _gen_qlit(self, name, mtype, obj, ifcond):
>>          if mtype not in ('command', 'event', 'builtin', 'array'):
>>              name = self._name(name)
>>          obj['name'] = name
>>          obj['meta-type'] = mtype
>> -        self._qlits.append(obj)
>> +        self._qlits.append((obj, ifcond))
>>
>>      def _gen_member(self, member):
>>          ret = {'name': member.name, 'type': self._use_type(member.type)}
>> @@ -134,38 +140,40 @@ const QLitObject %(c_name)s = %(c_string)s;
>>          return {'case': variant.name, 'type': self._use_type(variant.type)}
>>
>>      def visit_builtin_type(self, name, info, json_type):
>> -        self._gen_qlit(name, 'builtin', {'json-type': json_type})
>> +        self._gen_qlit(name, 'builtin', {'json-type': json_type}, None)
>>
>> -    def visit_enum_type(self, name, info, values, prefix):
>> -        self._gen_qlit(name, 'enum', {'values': values})
>> +    def visit_enum_type(self, name, info, values, prefix, ifcond):
>> +        self._gen_qlit(name, 'enum', {'values': values}, ifcond)
>>
>> -    def visit_array_type(self, name, info, element_type):
>> +    def visit_array_type(self, name, info, element_type, ifcond):
>>          element = self._use_type(element_type)
>> -        self._gen_qlit('[' + element + ']', 'array', {'element-type': 
>> element})
>> +        self._gen_qlit('[' + element + ']', 'array', {'element-type': 
>> element},
>> +                       ifcond)
>>
>> -    def visit_object_type_flat(self, name, info, members, variants):
>> +    def visit_object_type_flat(self, name, info, members, variants, ifcond):
>>          obj = {'members': [self._gen_member(m) for m in members]}
>>          if variants:
>>              obj.update(self._gen_variants(variants.tag_member.name,
>>                                            variants.variants))
>> -        self._gen_qlit(name, 'object', obj)
>> +        self._gen_qlit(name, 'object', obj, ifcond)
>>
>> -    def visit_alternate_type(self, name, info, variants):
>> +    def visit_alternate_type(self, name, info, variants, ifcond):
>>          self._gen_qlit(name, 'alternate',
>>                         {'members': [{'type': self._use_type(m.type)}
>> -                                    for m in variants.variants]})
>> +                                    for m in variants.variants]}, ifcond)
>>
>>      def visit_command(self, name, info, arg_type, ret_type,
>> -                      gen, success_response, boxed):
>> +                      gen, success_response, boxed, ifcond):
>>          arg_type = arg_type or self._schema.the_empty_object_type
>>          ret_type = ret_type or self._schema.the_empty_object_type
>>          self._gen_qlit(name, 'command',
>>                         {'arg-type': self._use_type(arg_type),
>> -                        'ret-type': self._use_type(ret_type)})
>> +                        'ret-type': self._use_type(ret_type)}, ifcond)
>>
>> -    def visit_event(self, name, info, arg_type, boxed):
>> +    def visit_event(self, name, info, arg_type, boxed, ifcond):
>>          arg_type = arg_type or self._schema.the_empty_object_type
>> -        self._gen_qlit(name, 'event', {'arg-type': 
>> self._use_type(arg_type)})
>> +        self._gen_qlit(name, 'event', {'arg-type': 
>> self._use_type(arg_type)},
>> +                       ifcond)
>>
>>  # Debugging aid: unmask QAPI schema's type names
>>  # We normally mask them, because they're not QMP wire ABI
>
> Out of review brainpower for today.  Hope to resume tomorrow.
>
> [...]
>



-- 
Marc-André Lureau



reply via email to

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