diff mbox

[v10,10/25] qapi: Improve generated event use of qapi visitor

Message ID 1454075341-13658-11-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake Jan. 29, 2016, 1:48 p.m. UTC
All other successful clients of visit_start_struct() were paired
with an unconditional visit_end_struct(); but the generated
code for events was relying on qmp_output_visitor_cleanup() to
work on an incomplete visit.  Alter the code to guarantee that
the struct is completed, which will make a future patch to
split visit_end_struct() easier to reason about.  While at it,
drop some assertions and comments that are not present in other
uses of the qmp output visitor, and pass NULL rather than "" as
the 'kind' parameter (matching most other uses where obj is NULL).

The changes to the generated code look like:

|     qov = qmp_output_visitor_new();
|-    g_assert(qov);
|-
|     v = qmp_output_get_visitor(qov);
|-    g_assert(v);
|
|-    /* Fake visit, as if all members are under a structure */
|-    visit_start_struct(v, NULL, "", "ACPI_DEVICE_OST", 0, &err);
|+    visit_start_struct(v, NULL, NULL, "ACPI_DEVICE_OST", 0, &err);
|     if (err) {
|         goto out;
|     }
|     visit_type_ACPIOSTInfo(v, &info, "info", &err);
|     if (err) {
|-        goto out;
|+        goto out_obj;
|     }
|-    visit_end_struct(v, &err);
|+out_obj:
|+    visit_end_struct(v, err ? NULL : &err);
|     if (err) {
|         goto out;
|     }
|
|     obj = qmp_output_get_qobject(qov);
|-    g_assert(obj != NULL);
|+    g_assert(obj);
|
|     qdict_put_obj(qmp, "data", obj);
|     emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
|
|out:
|     qmp_output_visitor_cleanup(qov);
|     error_propagate(errp, err);

Note that the 'goto out_obj' with no intervening code before the
label, as well as the construct of 'err ? NULL : &err', are both
a bit unusual but also temporary; they get fixed in a later patch
that splits visit_end_struct() to drop its errp parameter by moving
some checking before the label.  But until that time, this was the
simplest way to avoid the appearance of passing a possibly-set
error to visit_end_struct(), even though actual code inspection
shows that visit_end_struct() for a QMP output visitor will never
set an error.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v10: avoid appearance of &err misuse; enhance commit message
v9: save churn in declaration order for later series on boxed params,
drop Marc-Andre's R-b
v8: no change
v7: place earlier in series, adjust handling of 'kind'
v6: new patch

If desired, I can defer the hunk re-ordering the declaration of
obj to later in the series where it actually comes in handy.
---
 scripts/qapi-event.py | 16 +++++++---------
 scripts/qapi.py       |  5 +++--
 2 files changed, 10 insertions(+), 11 deletions(-)

Comments

Markus Armbruster Feb. 1, 2016, 12:31 p.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> All other successful clients of visit_start_struct() were paired
> with an unconditional visit_end_struct(); but the generated
> code for events was relying on qmp_output_visitor_cleanup() to
> work on an incomplete visit.  Alter the code to guarantee that
> the struct is completed, which will make a future patch to
> split visit_end_struct() easier to reason about.  While at it,
> drop some assertions and comments that are not present in other
> uses of the qmp output visitor, and pass NULL rather than "" as
> the 'kind' parameter (matching most other uses where obj is NULL).
>
> The changes to the generated code look like:
>
> |     qov = qmp_output_visitor_new();
> |-    g_assert(qov);
> |-
> |     v = qmp_output_get_visitor(qov);
> |-    g_assert(v);
> |
> |-    /* Fake visit, as if all members are under a structure */
> |-    visit_start_struct(v, NULL, "", "ACPI_DEVICE_OST", 0, &err);
> |+    visit_start_struct(v, NULL, NULL, "ACPI_DEVICE_OST", 0, &err);
> |     if (err) {
> |         goto out;
> |     }
> |     visit_type_ACPIOSTInfo(v, &info, "info", &err);
> |     if (err) {
> |-        goto out;
> |+        goto out_obj;
> |     }
> |-    visit_end_struct(v, &err);
> |+out_obj:
> |+    visit_end_struct(v, err ? NULL : &err);

Slightly awkward example, because out_obj is pointless in this
degenerated case.  You could pick one with multiple members (thus
multiple goto out_obj), or do pseudo-code hinting at multiple members.

> |     if (err) {
> |         goto out;
> |     }
> |
> |     obj = qmp_output_get_qobject(qov);
> |-    g_assert(obj != NULL);
> |+    g_assert(obj);
> |
> |     qdict_put_obj(qmp, "data", obj);
> |     emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
> |
> |out:
> |     qmp_output_visitor_cleanup(qov);
> |     error_propagate(errp, err);
>
> Note that the 'goto out_obj' with no intervening code before the
> label, as well as the construct of 'err ? NULL : &err', are both
> a bit unusual but also temporary; they get fixed in a later patch
> that splits visit_end_struct() to drop its errp parameter by moving
> some checking before the label.  But until that time, this was the
> simplest way to avoid the appearance of passing a possibly-set
> error to visit_end_struct(), even though actual code inspection
> shows that visit_end_struct() for a QMP output visitor will never
> set an error.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v10: avoid appearance of &err misuse; enhance commit message
> v9: save churn in declaration order for later series on boxed params,
> drop Marc-Andre's R-b
> v8: no change
> v7: place earlier in series, adjust handling of 'kind'
> v6: new patch
>
> If desired, I can defer the hunk re-ordering the declaration of
> obj to later in the series where it actually comes in handy.

Patch looks good.
Eric Blake Feb. 1, 2016, 10:50 p.m. UTC | #2
On 02/01/2016 05:31 AM, Markus Armbruster wrote:

>> |+    visit_start_struct(v, NULL, NULL, "ACPI_DEVICE_OST", 0, &err);
>> |     if (err) {
>> |         goto out;
>> |     }
>> |     visit_type_ACPIOSTInfo(v, &info, "info", &err);
>> |     if (err) {
>> |-        goto out;
>> |+        goto out_obj;
>> |     }
>> |-    visit_end_struct(v, &err);
>> |+out_obj:
>> |+    visit_end_struct(v, err ? NULL : &err);
> 
> Slightly awkward example, because out_obj is pointless in this
> degenerated case.  You could pick one with multiple members (thus
> multiple goto out_obj), or do pseudo-code hinting at multiple members.

DEVICE_DELETED, DEVICE_TRAY_MOVED, MEM_UNPLUG_ERROR,
NET_RX_FILTER_CHANGED, and SPICE_CONNECTED are nice candidates (two
members instead of one).  Do you want to take care of redoing any
portion of the commit message?
Markus Armbruster Feb. 2, 2016, 7:52 a.m. UTC | #3
Eric Blake <eblake@redhat.com> writes:

> On 02/01/2016 05:31 AM, Markus Armbruster wrote:
>
>>> |+    visit_start_struct(v, NULL, NULL, "ACPI_DEVICE_OST", 0, &err);
>>> |     if (err) {
>>> |         goto out;
>>> |     }
>>> |     visit_type_ACPIOSTInfo(v, &info, "info", &err);
>>> |     if (err) {
>>> |-        goto out;
>>> |+        goto out_obj;
>>> |     }
>>> |-    visit_end_struct(v, &err);
>>> |+out_obj:
>>> |+    visit_end_struct(v, err ? NULL : &err);
>> 
>> Slightly awkward example, because out_obj is pointless in this
>> degenerated case.  You could pick one with multiple members (thus
>> multiple goto out_obj), or do pseudo-code hinting at multiple members.
>
> DEVICE_DELETED, DEVICE_TRAY_MOVED, MEM_UNPLUG_ERROR,
> NET_RX_FILTER_CHANGED, and SPICE_CONNECTED are nice candidates (two
> members instead of one).  Do you want to take care of redoing any
> portion of the commit message?

Can do.  Unless something comes along that requires a respin.
diff mbox

Patch

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 720486f..0f5534f 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -2,7 +2,7 @@ 
 # QAPI event generator
 #
 # Copyright (c) 2014 Wenchao Xia
-# Copyright (c) 2015 Red Hat Inc.
+# Copyright (c) 2015-2016 Red Hat Inc.
 #
 # Authors:
 #  Wenchao Xia <wenchaoqemu@gmail.com>
@@ -61,25 +61,23 @@  def gen_event_send(name, arg_type):
     if arg_type and arg_type.members:
         ret += mcgen('''
     qov = qmp_output_visitor_new();
-    g_assert(qov);
-
     v = qmp_output_get_visitor(qov);
-    g_assert(v);

-    /* Fake visit, as if all members are under a structure */
-    visit_start_struct(v, NULL, "", "%(name)s", 0, &err);
+    visit_start_struct(v, NULL, NULL, "%(name)s", 0, &err);
 ''',
                      name=name)
         ret += gen_err_check()
-        ret += gen_visit_fields(arg_type.members, need_cast=True)
+        ret += gen_visit_fields(arg_type.members, need_cast=True,
+                                label='out_obj')
         ret += mcgen('''
-    visit_end_struct(v, &err);
+out_obj:
+    visit_end_struct(v, err ? NULL : &err);
     if (err) {
         goto out;
     }

     obj = qmp_output_get_qobject(qov);
-    g_assert(obj != NULL);
+    g_assert(obj);

     qdict_put_obj(qmp, "data", obj);
 ''')
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 37aa6fe..43b3251 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1636,7 +1636,8 @@  def gen_err_check(label='out', skiperr=False):
                  label=label)


-def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False):
+def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False,
+                     label='out'):
     ret = ''
     if skiperr:
         errparg = 'NULL'
@@ -1664,7 +1665,7 @@  def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False):
                      c_type=memb.type.c_name(), prefix=prefix, cast=cast,
                      c_name=c_name(memb.name), name=memb.name,
                      errp=errparg)
-        ret += gen_err_check(skiperr=skiperr)
+        ret += gen_err_check(skiperr=skiperr, label=label)

         if memb.optional:
             pop_indent()