All of lore.kernel.org
 help / color / mirror / Atom feed
From: Markus Armbruster <armbru@redhat.com>
To: Kevin Wolf <kwolf@redhat.com>
Cc: jsnow@redhat.com, qemu-devel@nongnu.org
Subject: Re: [PATCH v2 4/6] qapi: Apply aliases in qobject-input-visitor
Date: Fri, 19 Feb 2021 10:11:27 +0100	[thread overview]
Message-ID: <878s7kpglc.fsf@dusky.pond.sub.org> (raw)
In-Reply-To: <20210218161007.GA10998@merkur.fritz.box> (Kevin Wolf's message of "Thu, 18 Feb 2021 17:10:07 +0100")

Kevin Wolf <kwolf@redhat.com> writes:

> Am 18.02.2021 um 14:39 hat Markus Armbruster geschrieben:
>> Kevin Wolf <kwolf@redhat.com> writes:
>> 
>> > Am 17.02.2021 um 16:32 hat Markus Armbruster geschrieben:
>> >> Kevin Wolf <kwolf@redhat.com> writes:
>> >> 
>> >> > When looking for an object in a struct in the external representation,
>> >> > check not only the currently visited struct, but also whether an alias
>> >> > in the current StackObject matches and try to fetch the value from the
>> >> > alias then. Providing two values for the same object through different
>> >> > aliases is an error.
>> >> >
>> >> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
>> >> 
>> >> Looking just at qobject_input_try_get_object() for now.
>> >
>> > :-(
>> >
>> > This patch doesn't even feel that complicated to me.
>> 
>> I suspect it's just me having an unusually obtuse week.
>> 
>> The code is straightforward enough.  What I'm missing is a bit of "how
>> does this accomplish the goal" and "why is this safe" here and there.
>> 
>> > Old: Get the value from the QDict of the current StackObject with the
>> > given name.
>> >
>> > New: First do alias resolution (with find_object_member), which results
>> > in a StackObject and a name, and that's the QDict and key where you get
>> > the value from.
>> 
>> This part I understand.
>> 
>> We simultaneously walk the QAPI type and the input QObject, consuming
>> input as we go.
>> 
>> Whenever the walk leaves a QAPI object type, we check the corresponding
>> QObject has been consumed completely.
>> 
>> With aliases, we additionally look for input in a certain enclosing
>> input object (i.e. up the recursion stack).  If found, consume.
>> 
>> > Minor complication: Aliases can refer to members of nested objects that
>> > may not be provided in the input. But we want these to work.
>> >
>> > For example, my chardev series defines aliases to flatten
>> > SocketAddressLegacy (old syntax, I haven't rebased it yet):
>> >
>> > { 'union': 'SocketAddressLegacy',
>> >   'data': {
>> >     'inet': 'InetSocketAddress',
>> >     'unix': 'UnixSocketAddress',
>> >     'vsock': 'VsockSocketAddress',
>> >     'fd': 'String' },
>> >   'aliases': [
>> >     {'source': ['data']},
>> >     {'alias': 'fd', 'source': ['data', 'str']}
>> >   ]}
>> >
>> > Of course, the idea is that this input should work:
>> >
>> > { 'type': 'inet', 'hostname': 'localhost', 'port': '1234' }
>> >
>> > However, without implicit objects, parsing 'data' fails because it
>> > expects an object, but none is given (we specified all of its members on
>> > the top level through aliases). What we would have to give is:
>> >
>> > { 'type': 'inet', 'hostname': 'localhost', 'port': '1234', 'data': {} }
>> >
>> > And _that_ would work. Visiting 'data' succeeds because we now have the
>> > object that the visitor expects, and when visiting its members, the
>> > aliases fill in all of the mandatory values.
>> >
>> > So what this patch does is to implicitly assume the 'data': {} instead
>> > of erroring out when we know that aliases exist that can still provide
>> > values for the content of 'data'.
>> 
>> Aliases exist than can still provide, but will they?  What if input is
>> 
>>     {"type": "inet"}
>> 
>> ?
>> 
>> Your explanation makes me guess this is equivalent to
>> 
>>     {"type": "inet", "data": {}}
>> 
>> which fails the visit, because mandatory members of "data" are missing.
>> Fine.
>
> Okay, if you want the gory details, then the answer is yes for this
> case, but it depends.

I'm afraid I need the gory details to get over the review hump.

> If we're aliasing a single member, then we can easily check whether the
> alias is actually specified. If it's not in the input, no implicit
> object.

I figure this check is in the code parts I haven't gotten to, yet.  Not
your fault.

> But in our example, it is a wildcard alias and we don't know yet which
> aliases it will use. This depends on what the visitor for the implicit
> object will do (future tense).
>
>> If we make the members optional, it succeeds: qobject_input_optional()
>> checks both the regular and the aliased input, finds neither, and
>> returns false.  Still fine.
>> 
>> What if "data" is optional, too?  Hmmm...
>
> Yes, don't use optional objects in the middle of the path of a wildcard
> alias unless there is no semantic difference between empty object and
> absent object.

Aha!  So my spidey-sense wasn't entirely wrong.

>                This is documented in the code, but it might actually
> still be missing from qapi-code-gen.txt.

I can't find it there.  Needs fixing, obviously.

I guess checking "path of a wildcard alias crosses optional objects" is
hard (impractical?) for the same reasons checking "alias can't resolve"
is.

I'd expect "alias can't resolve" to be caused by typos, incomplete
renames, and such.  Basic testing should catch at least the typos.  Not
ideal, but I guess it'll do, at least for now.

Relying on testing to catch "crosses optional objects" mistakes feels
iffier to me, because it takes more careful tests.

Ham-fisted way to make basic tests catch it: *ignore* optional objects
when resolving aliases.  Is this a bad idea?

>> Example:
>> 
>>     { 'struct': 'Outer',
>>       'data': { '*inner': 'Inner' },
>> 
>>     { 'struct': 'Inner',
>>       'data': { '*true-name': 'str' } }
>> 
>> For input {}, we get an Outer object with
>> 
>>     .has_inner = false,
>>     .inner = NULL
>> 
>> Now add
>> 
>>       'aliases': [ { 'name': 'alias-name',
>>                      'source': [ 'inner', 'true-name' ] } ] }
>> 
>> What do we get now?  Is it
>> 
>>      .has_inner = true,
>>      .inner = { .has_true_name = false,
>>                 .true_name = NULL }
>> 
>> perhaps?
>
> I think this is the result you would get if you had used a wildcard
> alias. But since you used a single-member alias, we would see that
> 'alias-name' is not in the input and actually still return the original
> result:
>
>     .has_inner = false,
>     .inner = NULL

I wasn't aware there's a difference.  Now I am.

Thanks!



  reply	other threads:[~2021-02-19  9:13 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-11 18:31 [PATCH v2 0/6] qapi: Add support for aliases Kevin Wolf
2021-02-11 18:31 ` [PATCH v2 1/6] qapi: Add interfaces for alias support to Visitor Kevin Wolf
2021-02-16 11:56   ` Markus Armbruster
2021-02-11 18:31 ` [PATCH v2 2/6] qapi: Remember alias definitions in qobject-input-visitor Kevin Wolf
2021-02-16 12:06   ` Markus Armbruster
2021-02-11 18:31 ` [PATCH v2 3/6] qapi: Simplify full_name_nth() " Kevin Wolf
2021-02-16 12:22   ` Markus Armbruster
2021-02-11 18:31 ` [PATCH v2 4/6] qapi: Apply aliases " Kevin Wolf
2021-02-17 15:32   ` Markus Armbruster
2021-02-17 17:50     ` Kevin Wolf
2021-02-18 13:39       ` Markus Armbruster
2021-02-18 16:10         ` Kevin Wolf
2021-02-19  9:11           ` Markus Armbruster [this message]
2021-02-19 13:07             ` Markus Armbruster
2021-02-19 14:42   ` Markus Armbruster
2021-02-24  8:28   ` Markus Armbruster
2021-02-11 18:31 ` [PATCH v2 5/6] qapi: Add support for aliases Kevin Wolf
2021-02-16 15:43   ` Markus Armbruster
2021-02-17 15:23   ` Markus Armbruster
2021-02-17 16:17     ` Kevin Wolf
2021-02-18 10:26       ` Markus Armbruster
2021-02-11 18:31 ` [PATCH v2 6/6] tests/qapi-schema: Test cases " Kevin Wolf
2021-02-16 15:14   ` Markus Armbruster
2021-02-16 15:31     ` Kevin Wolf
2021-02-16 16:14       ` Markus Armbruster
2021-02-17 12:23         ` Markus Armbruster
2021-02-24  8:45 ` [PATCH v2 0/6] qapi: Add support " Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=878s7kpglc.fsf@dusky.pond.sub.org \
    --to=armbru@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.