All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E)
@ 2016-01-19 16:10 Eric Blake
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
                   ` (37 more replies)
  0 siblings, 38 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Eduardo Habkost

Based on qemu.git master. Pending prerequisites:
+ Not a strong dependency, but for qapi-tests to consistently pass,
I needed a race fixed:
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01827.html

Also available as a tag at this location:
git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv9e

and will soon be part of my branch with the rest of the v5 series, at:
http://repo.or.cz/qemu/ericb.git/shortlog/refs/heads/qapi

v9 notes:
Rebase to master, incorporate findings from Marc-André.

001/37:[----] [--] 'qobject: Document more shortcomings in our number handling'
002/37:[----] [--] 'qapi: Avoid use of misnamed DO_UPCAST()'
003/37:[----] [--] 'qapi: Drop dead dealloc visitor variable'
004/37:[----] [--] 'hmp: Improve use of qapi visitor'
005/37:[----] [--] 'vl: Improve use of qapi visitor'
006/37:[----] [--] 'balloon: Improve use of qapi visitor'
007/37:[0002] [FC] 'qapi: Improve generated event use of qapi visitor'
008/37:[----] [--] 'qapi: Track all failures between visit_start/stop'
009/37:[0005] [FC] 'qapi: Prefer type_int64 over type_int in visitors'
010/37:[0009] [FC] 'qapi: Make all visitors supply uint64 callbacks'
011/37:[----] [--] 'qapi: Consolidate visitor small integer callbacks'
012/37:[----] [--] 'qapi: Don't cast Enum* to int*'
013/37:[----] [--] 'qom: Use typedef for Visitor'
014/37:[0004] [FC] 'qapi: Swap visit_* arguments for consistent 'name' placement'
015/37:[----] [-C] 'qom: Swap 'name' next to visitor in ObjectPropertyAccessor'
016/37:[----] [--] 'qapi: Swap 'name' in visit_* callbacks to match public API'
017/37:[----] [--] 'qapi: Drop unused 'kind' for struct/enum visit'
018/37:[----] [--] 'qapi: Drop unused error argument for list and implicit struct'
019/37:[----] [--] 'qmp: Fix reference-counting of qnull on empty output visit'
020/37:[----] [--] 'qmp: Don't abuse stack to track qmp-output root'
021/37:[----] [--] 'qapi: Document visitor interfaces, add assertions'
022/37:[----] [--] 'qapi: Add visit_type_null() visitor'
023/37:[down] 'qmp: Support explicit null during input visit'
024/37:[0015] [FC] 'qmp: Tighten output visitor rules'
025/37:[----] [--] 'spapr_drc: Expose 'null' in qom-get when there is no fdt'
026/37:[----] [--] 'qapi: Simplify excess input reporting in input visitors'
027/37:[----] [--] 'qapi: Add type.is_empty() helper'
028/37:[----] [--] 'qapi: Fix command with named empty argument type'
029/37:[----] [--] 'qapi: Eliminate empty visit_type_FOO_fields'
030/37:[0013] [FC] 'qapi: Canonicalize missing object to :empty'
031/37:[----] [--] 'qapi-visit: Unify struct and union visit'
032/37:[----] [--] 'qapi: Rework deallocation of partial struct'
033/37:[0003] [FC] 'qapi: Split visit_end_struct() into pieces'
034/37:[----] [--] 'qapi: Simplify semantics of visit_next_list()'
035/37:[0006] [FC] 'qapi: Change visit_type_FOO() to no longer return partial objects'
036/37:[----] [--] 'RFC: qapi: Adjust layout of FooList types'
037/37:[down] 'qapi: Update docs to match recent generator changes'

v8 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg03863.html
Four new patches (13-16/35), plus rebasing on top of them, so that
the code base now consistently passes a 'v, name' pair anywhere a
visitor needs a name, rather than putting other arguments in between
the pair. I got to have fun with Coccinelle   Also fix a bug in my
changes to visit_next_list() (v7 29/31), so that 'make check' and
qemu-iotests now pass at all points in the series.

The parameter ordering changes have the potential to be a rebase
magnet, so I'm hoping this series can go in relatively soon after
Markus returns from break.

I made good on my threat in v7 of writing a qapi-to-JSON output
visitor, but that will remain a separate series based on this one
(the only posting of that series so far now needs rebasing:
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01760.html)

v7 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01181.html

v6 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg05793.html

v5 and earlier - look in the mail archives

Eric Blake (37):
  qobject: Document more shortcomings in our number handling
  qapi: Avoid use of misnamed DO_UPCAST()
  qapi: Drop dead dealloc visitor variable
  hmp: Improve use of qapi visitor
  vl: Improve use of qapi visitor
  balloon: Improve use of qapi visitor
  qapi: Improve generated event use of qapi visitor
  qapi: Track all failures between visit_start/stop
  qapi: Prefer type_int64 over type_int in visitors
  qapi: Make all visitors supply uint64 callbacks
  qapi: Consolidate visitor small integer callbacks
  qapi: Don't cast Enum* to int*
  qom: Use typedef for Visitor
  qapi: Swap visit_* arguments for consistent 'name' placement
  qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  qapi: Swap 'name' in visit_* callbacks to match public API
  qapi: Drop unused 'kind' for struct/enum visit
  qapi: Drop unused error argument for list and implicit struct
  qmp: Fix reference-counting of qnull on empty output visit
  qmp: Don't abuse stack to track qmp-output root
  qapi: Document visitor interfaces, add assertions
  qapi: Add visit_type_null() visitor
  qmp: Support explicit null during input visit
  qmp: Tighten output visitor rules
  spapr_drc: Expose 'null' in qom-get when there is no fdt
  qapi: Simplify excess input reporting in input visitors
  qapi: Add type.is_empty() helper
  qapi: Fix command with named empty argument type
  qapi: Eliminate empty visit_type_FOO_fields
  qapi: Canonicalize missing object to :empty
  qapi-visit: Unify struct and union visit
  qapi: Rework deallocation of partial struct
  qapi: Split visit_end_struct() into pieces
  qapi: Simplify semantics of visit_next_list()
  qapi: Change visit_type_FOO() to no longer return partial objects
  RFC: qapi: Adjust layout of FooList types
  qapi: Update docs to match recent generator changes

 backends/hostmem.c                      |  24 +--
 block/qapi.c                            |   2 +-
 blockdev.c                              |   4 +-
 bootdevice.c                            |  12 +-
 docs/qapi-code-gen.txt                  | 130 +++++++-----
 hmp.c                                   |  23 ++-
 hw/acpi/core.c                          |   4 +-
 hw/acpi/ich9.c                          |  49 ++---
 hw/block/nvme.c                         |  12 +-
 hw/core/machine.c                       |  24 +--
 hw/core/qdev-properties-system.c        |  44 ++--
 hw/core/qdev-properties.c               | 180 ++++++++--------
 hw/core/qdev.c                          |   7 +-
 hw/i386/pc.c                            |  43 ++--
 hw/ide/qdev.c                           |  12 +-
 hw/intc/xics.c                          |  20 +-
 hw/isa/lpc_ich9.c                       |   7 +-
 hw/mem/pc-dimm.c                        |   6 +-
 hw/misc/edu.c                           |   6 +-
 hw/misc/tmp105.c                        |  12 +-
 hw/net/ne2000-isa.c                     |  14 +-
 hw/pci-host/piix.c                      |  18 +-
 hw/pci-host/q35.c                       |  23 +--
 hw/ppc/spapr_drc.c                      |  43 ++--
 hw/usb/dev-storage.c                    |  12 +-
 hw/virtio/virtio-balloon.c              |  43 ++--
 include/qapi/qmp-output-visitor.h       |   1 +
 include/qapi/visitor-impl.h             | 106 ++++++----
 include/qapi/visitor.h                  | 298 ++++++++++++++++++++++++---
 include/qom/object.h                    |  13 +-
 memory.c                                |  26 +--
 net/dump.c                              |  12 +-
 net/filter-buffer.c                     |  14 +-
 net/net.c                               |   4 +-
 numa.c                                  |   6 +-
 qapi/opts-visitor.c                     | 121 ++++++-----
 qapi/qapi-dealloc-visitor.c             | 108 ++++------
 qapi/qapi-visit-core.c                  | 352 +++++++++++++++++---------------
 qapi/qmp-input-visitor.c                | 188 ++++++++++-------
 qapi/qmp-output-visitor.c               | 166 +++++++--------
 qapi/string-input-visitor.c             |  93 +++++----
 qapi/string-output-visitor.c            |  88 ++++----
 qemu-img.c                              |  11 +-
 qobject/json-parser.c                   |   4 +-
 qobject/qjson.c                         |   8 +-
 qom/object.c                            | 129 ++++++------
 replay/replay-input.c                   |   4 +-
 scripts/qapi-commands.py                |  21 +-
 scripts/qapi-event.py                   |  22 +-
 scripts/qapi-types.py                   |  13 +-
 scripts/qapi-visit.py                   | 264 ++++++++++++------------
 scripts/qapi.py                         |  37 ++--
 target-i386/cpu.c                       |  96 ++++-----
 target-ppc/translate_init.c             |  12 +-
 tests/qapi-schema/event-case.out        |   2 +-
 tests/qapi-schema/flat-union-empty.out  |   1 +
 tests/qapi-schema/ident-with-escape.out |   1 +
 tests/qapi-schema/indented-expr.out     |   4 +-
 tests/qapi-schema/qapi-schema-test.json |   2 +
 tests/qapi-schema/qapi-schema-test.out  |  47 ++++-
 tests/qapi-schema/union-clash-data.out  |   2 +
 tests/qapi-schema/union-empty.out       |   1 +
 tests/test-opts-visitor.c               |   6 +-
 tests/test-qdev-global-props.c          |  18 +-
 tests/test-qmp-commands.c               |  20 +-
 tests/test-qmp-input-strict.c           |  47 ++---
 tests/test-qmp-input-visitor.c          | 117 ++++++-----
 tests/test-qmp-output-visitor.c         |  38 ++--
 tests/test-string-input-visitor.c       |  34 +--
 tests/test-string-output-visitor.c      |  16 +-
 tests/test-visitor-serialization.c      |  54 ++---
 util/qemu-sockets.c                     |   4 +-
 vl.c                                    |  29 +--
 73 files changed, 1920 insertions(+), 1514 deletions(-)

-- 
2.5.0

^ permalink raw reply	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20  9:02   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
                   ` (36 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Luiz Capitulino

We've already documented that our JSON parsing is locale dependent;
but we should also document that our JSON output has the same
problem.  Additionally, JSON requires finite values (you have to
upgrade to JSON5 to get support for Inf or NaN), and our output
risks truncating floating point numbers to the point of losing
significant precision.

Sadly, this series is not going to be the one that addresses these
problems.

Fix some trailing whitespace I noticed in the vicinity.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: new patch
---
 qobject/json-parser.c | 4 +++-
 qobject/qjson.c       | 8 +++++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index 3c5d35d..6ab98a7 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -1,5 +1,5 @@
 /*
- * JSON Parser 
+ * JSON Parser
  *
  * Copyright IBM, Corp. 2009
  *
@@ -519,6 +519,8 @@ static QObject *parse_literal(JSONParserContext *ctxt)
     }
     case JSON_FLOAT:
         /* FIXME dependent on locale */
+        /* FIXME our lexer matches RFC7159 in forbidding Inf or NaN,
+         * but those might be useful extensions beyond JSON */
         return QOBJECT(qfloat_from_double(strtod(token->str, NULL)));
     default:
         abort();
diff --git a/qobject/qjson.c b/qobject/qjson.c
index a3e6a7c..41d9d65 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -237,6 +237,12 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         char buffer[1024];
         int len;

+        /* FIXME: snprintf() is locale dependent; but JSON requires
+         * numbers to be formatted as if in the C locale. */
+        /* FIXME: This risks printing Inf or NaN, which are not valid
+         * JSON values. */
+        /* FIXME: the default precision of %f may be insufficient to
+         * tell this number apart from others. */
         len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val));
         while (len > 0 && buffer[len - 1] == '0') {
             len--;
@@ -247,7 +253,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         } else {
             buffer[len] = 0;
         }
-        
+
         qstring_append(str, buffer);
         break;
     }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST()
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 10:04   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 03/37] qapi: Drop dead dealloc visitor variable Eric Blake
                   ` (35 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

The macro DO_UPCAST() is incorrectly named: it converts from a
parent class to a derived class (which is a downcast).  Better,
and more consistent with some of the other qapi visitors, is
to use the container_of() macro through a to_FOO() helper.

Our current definition of container_of() is weaker than
DO_UPCAST(), in that it does not require the derived class to
have Visitor as its first member, but this does not hurt our
usage patterns in qapi visitors.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: new patch
---
 qapi/opts-visitor.c          | 28 +++++++++++++++++-----------
 qapi/string-input-visitor.c  | 23 ++++++++++++++---------
 qapi/string-output-visitor.c | 21 +++++++++++++--------
 3 files changed, 44 insertions(+), 28 deletions(-)

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index ef5fb8b..dd4094c 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -89,6 +89,12 @@ struct OptsVisitor
 };


+static OptsVisitor *to_ov(Visitor *v)
+{
+    return container_of(v, OptsVisitor, visitor);
+}
+
+
 static void
 destroy_list(gpointer list)
 {
@@ -121,7 +127,7 @@ static void
 opts_start_struct(Visitor *v, void **obj, const char *kind,
                   const char *name, size_t size, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;

     if (obj) {
@@ -160,7 +166,7 @@ ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
 static void
 opts_end_struct(Visitor *v, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     GQueue *any;

     if (--ov->depth > 0) {
@@ -202,7 +208,7 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
 static void
 opts_start_list(Visitor *v, const char *name, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);

     /* we can't traverse a list in a list */
     assert(ov->list_mode == LM_NONE);
@@ -216,7 +222,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
 static GenericList *
 opts_next_list(Visitor *v, GenericList **list, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     GenericList **link;

     switch (ov->list_mode) {
@@ -265,7 +271,7 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
 static void
 opts_end_list(Visitor *v, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);

     assert(ov->list_mode == LM_STARTED ||
            ov->list_mode == LM_IN_PROGRESS ||
@@ -307,7 +313,7 @@ processed(OptsVisitor *ov, const char *name)
 static void
 opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;

     opt = lookup_scalar(ov, name, errp);
@@ -323,7 +329,7 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
 static void
 opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;

     opt = lookup_scalar(ov, name, errp);
@@ -356,7 +362,7 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
 static void
 opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
     const char *str;
     long long val;
@@ -412,7 +418,7 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
 static void
 opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
     const char *str;
     unsigned long long val;
@@ -464,7 +470,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 static void
 opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
     int64_t val;
     char *endptr;
@@ -490,7 +496,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 static void
 opts_optional(Visitor *v, bool *present, const char *name)
 {
-    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+    OptsVisitor *ov = to_ov(v);

     /* we only support a single mandatory scalar field in a list node */
     assert(ov->list_mode == LM_NONE);
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index dee780a..7f5645b 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -32,6 +32,11 @@ struct StringInputVisitor
     const char *string;
 };

+static StringInputVisitor *to_siv(Visitor *v)
+{
+    return container_of(v, StringInputVisitor, visitor);
+}
+
 static void free_range(void *range, void *dummy)
 {
     g_free(range);
@@ -120,7 +125,7 @@ error:
 static void
 start_list(Visitor *v, const char *name, Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);

     parse_str(siv, errp);

@@ -136,7 +141,7 @@ start_list(Visitor *v, const char *name, Error **errp)
 static GenericList *
 next_list(Visitor *v, GenericList **list, Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);
     GenericList **link;
     Range *r;

@@ -175,14 +180,14 @@ next_list(Visitor *v, GenericList **list, Error **errp)
 static void
 end_list(Visitor *v, Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);
     siv->head = true;
 }

 static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
                            Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);

     if (!siv->string) {
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -224,7 +229,7 @@ error:
 static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
                             Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);
     Error *err = NULL;
     uint64_t val;

@@ -246,7 +251,7 @@ static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
 static void parse_type_bool(Visitor *v, bool *obj, const char *name,
                             Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);

     if (siv->string) {
         if (!strcasecmp(siv->string, "on") ||
@@ -270,7 +275,7 @@ static void parse_type_bool(Visitor *v, bool *obj, const char *name,
 static void parse_type_str(Visitor *v, char **obj, const char *name,
                            Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);
     if (siv->string) {
         *obj = g_strdup(siv->string);
     } else {
@@ -282,7 +287,7 @@ static void parse_type_str(Visitor *v, char **obj, const char *name,
 static void parse_type_number(Visitor *v, double *obj, const char *name,
                               Error **errp)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);
     char *endp = (char *) siv->string;
     double val;

@@ -301,7 +306,7 @@ static void parse_type_number(Visitor *v, double *obj, const char *name,

 static void parse_optional(Visitor *v, bool *present, const char *name)
 {
-    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+    StringInputVisitor *siv = to_siv(v);

     if (!siv->string) {
         *present = false;
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index b86ce2c..202764c 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -66,6 +66,11 @@ struct StringOutputVisitor
     GList *ranges;
 };

+static StringOutputVisitor *to_sov(Visitor *v)
+{
+    return container_of(v, StringOutputVisitor, visitor);
+}
+
 static void string_output_set(StringOutputVisitor *sov, char *string)
 {
     if (sov->string) {
@@ -119,7 +124,7 @@ static void format_string(StringOutputVisitor *sov, Range *r, bool next,
 static void print_type_int(Visitor *v, int64_t *obj, const char *name,
                            Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);
     GList *l;

     switch (sov->list_mode) {
@@ -195,7 +200,7 @@ static void print_type_int(Visitor *v, int64_t *obj, const char *name,
 static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
                            Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);
     static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' };
     uint64_t div, val;
     char *out;
@@ -226,14 +231,14 @@ static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
 static void print_type_bool(Visitor *v, bool *obj, const char *name,
                             Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);
     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
 }

 static void print_type_str(Visitor *v, char **obj, const char *name,
                            Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);
     char *out;

     if (sov->human) {
@@ -247,14 +252,14 @@ static void print_type_str(Visitor *v, char **obj, const char *name,
 static void print_type_number(Visitor *v, double *obj, const char *name,
                               Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);
     string_output_set(sov, g_strdup_printf("%f", *obj));
 }

 static void
 start_list(Visitor *v, const char *name, Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);

     /* we can't traverse a list in a list */
     assert(sov->list_mode == LM_NONE);
@@ -265,7 +270,7 @@ start_list(Visitor *v, const char *name, Error **errp)
 static GenericList *
 next_list(Visitor *v, GenericList **list, Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);
     GenericList *ret = NULL;
     if (*list) {
         if (sov->head) {
@@ -292,7 +297,7 @@ next_list(Visitor *v, GenericList **list, Error **errp)
 static void
 end_list(Visitor *v, Error **errp)
 {
-    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+    StringOutputVisitor *sov = to_sov(v);

     assert(sov->list_mode == LM_STARTED ||
            sov->list_mode == LM_END ||
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 03/37] qapi: Drop dead dealloc visitor variable
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor Eric Blake
                   ` (34 subsequent siblings)
  37 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Commit 0b9d8542 added StackEntry.is_list_head, but forgot to
delete the now-unused QapiDeallocVisitor.is_list_head.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: new patch
---
 qapi/qapi-dealloc-visitor.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 737deab..204de8f 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -28,7 +28,6 @@ struct QapiDeallocVisitor
 {
     Visitor visitor;
     QTAILQ_HEAD(, StackEntry) stack;
-    bool is_list_head;
 };

 static QapiDeallocVisitor *to_qov(Visitor *v)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (2 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 03/37] qapi: Drop dead dealloc visitor variable Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 13:05   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 05/37] vl: " Eric Blake
                   ` (33 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Luiz Capitulino

Cache the visitor in a local variable instead of repeatedly
calling the accessor.  Pass NULL for the visit_start_struct()
object (which matches the fact that we were already passing 0
for the size argument, because we aren't using the visit to
allocate a qapi struct).

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: place earlier in series, drop attempts to provide a 'kind' string,
drop bogus avoidance of qmp_object_del() on error
v6: new patch, split from RFC on v5 7/46
---
 hmp.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/hmp.c b/hmp.c
index 54f2620..6d67f9b 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1656,9 +1656,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
     QemuOpts *opts;
     char *type = NULL;
     char *id = NULL;
-    void *dummy = NULL;
     OptsVisitor *ov;
     QDict *pdict;
+    Visitor *v;

     opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
     if (err) {
@@ -1667,28 +1667,29 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)

     ov = opts_visitor_new(opts);
     pdict = qdict_clone_shallow(qdict);
+    v = opts_get_visitor(ov);

-    visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+    visit_start_struct(v, NULL, NULL, NULL, 0, &err);
     if (err) {
         goto out_clean;
     }

     qdict_del(pdict, "qom-type");
-    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+    visit_type_str(v, &type, "qom-type", &err);
     if (err) {
         goto out_end;
     }

     qdict_del(pdict, "id");
-    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+    visit_type_str(v, &id, "id", &err);
     if (err) {
         goto out_end;
     }

-    object_add(type, id, pdict, opts_get_visitor(ov), &err);
+    object_add(type, id, pdict, v, &err);

 out_end:
-    visit_end_struct(opts_get_visitor(ov), &err_end);
+    visit_end_struct(v, &err_end);
     if (!err && err_end) {
         qmp_object_del(id, NULL);
     }
@@ -1700,7 +1701,6 @@ out_clean:
     qemu_opts_del(opts);
     g_free(id);
     g_free(type);
-    g_free(dummy);

 out:
     hmp_handle_error(mon, &err);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 05/37] vl: Improve use of qapi visitor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (3 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 13:43   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 06/37] balloon: " Eric Blake
                   ` (32 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Paolo Bonzini

Cache the visitor in a local variable instead of repeatedly
calling the accessor.  Pass NULL for the visit_start_struct()
object (which matches the fact that we were already passing 0
for the size argument, because we aren't using the visit to
allocate a qapi struct).  Guarantee that visit_end_struct()
is called if visit_start_struct() succeeded.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: place earlier in series, drop attempts to provide a 'kind' string,
drop bogus avoidance of qmp_object_del() on error
v6: new patch, split from RFC on v5 7/46
---
 vl.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/vl.c b/vl.c
index f043009..aaa5403 100644
--- a/vl.c
+++ b/vl.c
@@ -2822,44 +2822,47 @@ static bool object_create_delayed(const char *type)
 static int object_create(void *opaque, QemuOpts *opts, Error **errp)
 {
     Error *err = NULL;
+    Error *err_end = NULL;
     char *type = NULL;
     char *id = NULL;
-    void *dummy = NULL;
     OptsVisitor *ov;
     QDict *pdict;
     bool (*type_predicate)(const char *) = opaque;
+    Visitor *v;

     ov = opts_visitor_new(opts);
     pdict = qemu_opts_to_qdict(opts, NULL);
+    v = opts_get_visitor(ov);

-    visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+    visit_start_struct(v, NULL, NULL, NULL, 0, &err);
     if (err) {
         goto out;
     }

     qdict_del(pdict, "qom-type");
-    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+    visit_type_str(v, &type, "qom-type", &err);
     if (err) {
         goto out;
     }
     if (!type_predicate(type)) {
+        visit_end_struct(v, NULL);
         goto out;
     }

     qdict_del(pdict, "id");
-    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+    visit_type_str(v, &id, "id", &err);
     if (err) {
-        goto out;
+        goto out_end;
     }

-    object_add(type, id, pdict, opts_get_visitor(ov), &err);
-    if (err) {
-        goto out;
-    }
-    visit_end_struct(opts_get_visitor(ov), &err);
-    if (err) {
+    object_add(type, id, pdict, v, &err);
+
+out_end:
+    visit_end_struct(v, &err_end);
+    if (!err && err_end) {
         qmp_object_del(id, NULL);
     }
+    error_propagate(&err, err_end);

 out:
     opts_visitor_cleanup(ov);
@@ -2867,7 +2870,6 @@ out:
     QDECREF(pdict);
     g_free(id);
     g_free(type);
-    g_free(dummy);
     if (err) {
         error_report_err(err);
         return -1;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 06/37] balloon: Improve use of qapi visitor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (4 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 05/37] vl: " Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 14:09   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event " Eric Blake
                   ` (31 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael S. Tsirkin

Rework the control flow of balloon_stats_get_all() to make it
easier for a later patch to split visit_end_struct().  Also
switch to the uint64 visitor to match the data type.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: place earlier in series
v6: new patch, split from RFC on v5 7/46
---
 hw/virtio/virtio-balloon.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 9671635..1ce987a 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -130,10 +130,13 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v,
     if (err) {
         goto out_end;
     }
-    for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
-        visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
-                         &err);
+    for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
+        visit_type_uint64(v, &s->stats[i], balloon_stat_names[i], &err);
+        if (err) {
+            goto out_nested;
+        }
     }
+out_nested:
     error_propagate(errp, err);
     err = NULL;
     visit_end_struct(v, &err);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event use of qapi visitor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (5 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 06/37] balloon: " Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 15:19   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop Eric Blake
                   ` (30 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

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

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

---
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 | 12 +++++-------
 scripts/qapi.py       |  5 +++--
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 720486f..761cda9 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -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('''
+out_obj:
     visit_end_struct(v, &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 7dec611..497eaba 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()
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (6 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event " Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 16:03   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors Eric Blake
                   ` (29 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Inside the generated code between visit_start_struct() and
visit_end_struct(), we were blindly setting the error into
the caller's errp parameter.  But a future patch to split
visit_end_struct() will require that we take action based
on whether an error has occurred, which requires us to track
all actions through a local err.  Rewrite the visits to be
more in line with the other generated calls.

Generated code changes look like:

|@@ -42,12 +42,18 @@ void visit_type_GuestAgentCommandInfo(Vi
|     Error *err = NULL;
|
|     visit_start_struct(v, (void **)obj, "GuestAgentCommandInfo", name, sizeof(GuestAgentCommandInfo), &err);
|-    if (!err) {
|-        if (*obj) {
|-            visit_type_GuestAgentCommandInfo_fields(v, obj, errp);
|-        }
|-        visit_end_struct(v, &err);
|+    if (err) {
|+        goto out;
|     }
|+    if (!*obj) {
|+        goto out_obj;
|+    }
|+    visit_type_GuestAgentCommandInfo_fields(v, obj, &err);
|+out_obj:
|+    error_propagate(errp, err);
|+    err = NULL;
|+    visit_end_struct(v, &err);
|+out:
|     error_propagate(errp, err);
| }

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: enhance commit message
v8: no change
v7: place earlier in series
v6: based loosely on v5 7/46, but mostly a rewrite to get the last
generated code in the same form as all the others, so that the
later conversion to split visit_check_struct() will be easier
---
 scripts/qapi-visit.py | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index b93690b..4a4f67d 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -123,12 +123,18 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
     Error *err = NULL;

     visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
-    if (!err) {
-        if (*obj) {
-            visit_type_%(c_name)s_fields(v, obj, errp);
-        }
-        visit_end_struct(v, &err);
+    if (err) {
+        goto out;
     }
+    if (!*obj) {
+        goto out_obj;
+    }
+    visit_type_%(c_name)s_fields(v, obj, &err);
+out_obj:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+out:
     error_propagate(errp, err);
 }
 ''',
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (7 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 17:07   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks Eric Blake
                   ` (28 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

The qapi builtin type 'int' is basically shorthand for the type
'int64'.  In fact, since no visitor was providing the optional
type_int64() callback, visit_type_int64() was just always falling
back to type_int(), cementing the equivalence between the types.

However, some visitors are providing a type_uint64() callback.
For purposes of code consistency, it is nicer if all visitors
use the paired type_int64/type_uint64 names rather than the
mismatched type_int/type_uint64.  So this patch just renames
the signed int callbacks in place, dropping the type_int()
callback as redundant, and a later patch will focus on the
unsigned int callbacks.

Add some FIXMEs to questionable reuse of errp in code touched
by the rename, while at it (the reuse works as long as the
callbacks don't modify value when setting an error, but it's not
a good example to set) - a later patch will then fix those.

No change in functionality here, although further cleanups are
in the pipeline.

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

---
v9: improve commit message, hoist in part of 11/35, drop
Marc-Andre's R-b
v8: no change
v7: split off of 1/23 and 2/23 for easier-to-read diffs
---
 include/qapi/visitor-impl.h  |  6 ++++--
 qapi/opts-visitor.c          |  4 ++--
 qapi/qapi-dealloc-visitor.c  |  6 +++---
 qapi/qapi-visit-core.c       | 36 ++++++++++++++++++++++--------------
 qapi/qmp-input-visitor.c     |  6 +++---
 qapi/qmp-output-visitor.c    |  6 +++---
 qapi/string-input-visitor.c  |  6 +++---
 qapi/string-output-visitor.c |  6 +++---
 8 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 44a21b7..c263a26 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -36,7 +36,10 @@ struct Visitor
     void (*get_next_type)(Visitor *v, QType *type, bool promote_int,
                           const char *name, Error **errp);

-    void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+    /* Must be set. */
+    void (*type_int64)(Visitor *v, int64_t *obj, const char *name,
+                       Error **errp);
+    /* Must be set. */
     void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
@@ -54,7 +57,6 @@ struct Visitor
     void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
     void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
     void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
-    void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp);
     /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */
     void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
     bool (*start_union)(Visitor *v, bool data_present, Error **errp);
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index dd4094c..56c798f 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -360,7 +360,7 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)


 static void
-opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
+opts_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -528,7 +528,7 @@ opts_visitor_new(const QemuOpts *opts)
      */
     ov->visitor.type_enum = &input_type_enum;

-    ov->visitor.type_int    = &opts_type_int;
+    ov->visitor.type_int64  = &opts_type_int64;
     ov->visitor.type_uint64 = &opts_type_uint64;
     ov->visitor.type_size   = &opts_type_size;
     ov->visitor.type_bool   = &opts_type_bool;
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 204de8f..e9b9f3f 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -135,8 +135,8 @@ static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
     }
 }

-static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name,
-                                  Error **errp)
+static void qapi_dealloc_type_int64(Visitor *v, int64_t *obj, const char *name,
+                                    Error **errp)
 {
 }

@@ -219,7 +219,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.next_list = qapi_dealloc_next_list;
     v->visitor.end_list = qapi_dealloc_end_list;
     v->visitor.type_enum = qapi_dealloc_type_enum;
-    v->visitor.type_int = qapi_dealloc_type_int;
+    v->visitor.type_int64 = qapi_dealloc_type_int64;
     v->visitor.type_bool = qapi_dealloc_type_bool;
     v->visitor.type_str = qapi_dealloc_type_str;
     v->visitor.type_number = qapi_dealloc_type_number;
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 6d63e40..6295fa8 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -97,7 +97,7 @@ void visit_type_enum(Visitor *v, int *obj, const char * const strings[],

 void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
 {
-    v->type_int(v, obj, name, errp);
+    v->type_int64(v, obj, name, errp);
 }

 void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
@@ -108,8 +108,10 @@ void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
         v->type_uint8(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         if (value < 0 || value > UINT8_MAX) {
+            /* FIXME questionable reuse of errp if type_int64() changes
+               value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "uint8_t");
             return;
@@ -126,8 +128,10 @@ void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp
         v->type_uint16(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         if (value < 0 || value > UINT16_MAX) {
+            /* FIXME questionable reuse of errp if type_int64() changes
+               value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "uint16_t");
             return;
@@ -144,8 +148,10 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp
         v->type_uint32(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         if (value < 0 || value > UINT32_MAX) {
+            /* FIXME questionable reuse of errp if type_int64() changes
+               value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "uint32_t");
             return;
@@ -162,7 +168,7 @@ void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp
         v->type_uint64(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         *obj = value;
     }
 }
@@ -175,8 +181,10 @@ void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
         v->type_int8(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         if (value < INT8_MIN || value > INT8_MAX) {
+            /* FIXME questionable reuse of errp if type_int64() changes
+               value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "int8_t");
             return;
@@ -193,8 +201,10 @@ void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
         v->type_int16(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         if (value < INT16_MIN || value > INT16_MAX) {
+            /* FIXME questionable reuse of errp if type_int64() changes
+               value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "int16_t");
             return;
@@ -211,8 +221,10 @@ void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
         v->type_int32(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         if (value < INT32_MIN || value > INT32_MAX) {
+            /* FIXME questionable reuse of errp if type_int64() changes
+               value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "int32_t");
             return;
@@ -223,11 +235,7 @@ void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)

 void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
 {
-    if (v->type_int64) {
-        v->type_int64(v, obj, name, errp);
-    } else {
-        v->type_int(v, obj, name, errp);
-    }
+    v->type_int64(v, obj, name, errp);
 }

 void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
@@ -240,7 +248,7 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
         v->type_uint64(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int(v, &value, name, errp);
+        v->type_int64(v, &value, name, errp);
         *obj = value;
     }
 }
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 932b5f3..0d8a3c3 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -224,8 +224,8 @@ static void qmp_input_get_next_type(Visitor *v, QType *type, bool promote_int,
     }
 }

-static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
-                               Error **errp)
+static void qmp_input_type_int64(Visitor *v, int64_t *obj, const char *name,
+                                 Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
@@ -341,7 +341,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.next_list = qmp_input_next_list;
     v->visitor.end_list = qmp_input_end_list;
     v->visitor.type_enum = input_type_enum;
-    v->visitor.type_int = qmp_input_type_int;
+    v->visitor.type_int64 = qmp_input_type_int64;
     v->visitor.type_bool = qmp_input_type_bool;
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 29899ac..3984011 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -158,8 +158,8 @@ static void qmp_output_end_list(Visitor *v, Error **errp)
     qmp_output_pop(qov);
 }

-static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name,
-                                Error **errp)
+static void qmp_output_type_int64(Visitor *v, int64_t *obj, const char *name,
+                                  Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     qmp_output_add(qov, name, qint_from_int(*obj));
@@ -241,7 +241,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.next_list = qmp_output_next_list;
     v->visitor.end_list = qmp_output_end_list;
     v->visitor.type_enum = output_type_enum;
-    v->visitor.type_int = qmp_output_type_int;
+    v->visitor.type_int64 = qmp_output_type_int64;
     v->visitor.type_bool = qmp_output_type_bool;
     v->visitor.type_str = qmp_output_type_str;
     v->visitor.type_number = qmp_output_type_number;
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 7f5645b..2f422f0 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -184,8 +184,8 @@ end_list(Visitor *v, Error **errp)
     siv->head = true;
 }

-static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
-                           Error **errp)
+static void parse_type_int64(Visitor *v, int64_t *obj, const char *name,
+                             Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);

@@ -335,7 +335,7 @@ StringInputVisitor *string_input_visitor_new(const char *str)
     v = g_malloc0(sizeof(*v));

     v->visitor.type_enum = input_type_enum;
-    v->visitor.type_int = parse_type_int;
+    v->visitor.type_int64 = parse_type_int64;
     v->visitor.type_size = parse_type_size;
     v->visitor.type_bool = parse_type_bool;
     v->visitor.type_str = parse_type_str;
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 202764c..c0a9331 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -121,8 +121,8 @@ static void format_string(StringOutputVisitor *sov, Range *r, bool next,
     }
 }

-static void print_type_int(Visitor *v, int64_t *obj, const char *name,
-                           Error **errp)
+static void print_type_int64(Visitor *v, int64_t *obj, const char *name,
+                             Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
     GList *l;
@@ -345,7 +345,7 @@ StringOutputVisitor *string_output_visitor_new(bool human)
     v->string = g_string_new(NULL);
     v->human = human;
     v->visitor.type_enum = output_type_enum;
-    v->visitor.type_int = print_type_int;
+    v->visitor.type_int64 = print_type_int64;
     v->visitor.type_size = print_type_size;
     v->visitor.type_bool = print_type_bool;
     v->visitor.type_str = print_type_str;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (8 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 17:29   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks Eric Blake
                   ` (27 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Our qapi visitor contract supports multiple integer visitors,
but left the type_uint64 visitor as optional (falling back on
type_int64); it also marks the type_size visitor as optional
(falling back on type_uint64 or even type_int64).

Note that the default of falling back on type_int for unsigned
visitors can cause confusing results for values larger than
INT64_MAX (such as having to pass in a negative 2s complement
value on input, and getting a negative result on output).

This patch does not fully address the disparity in handling
large values as negatives, but does move towards a cleaner
situation where EVERY visitor provides both type_int64 and
type_uint64 variants as entry points; then each client can
either implement sane differences between the two, or document
in place with a FIXME that there is munging going on.

The dealloc visitor no longer needs a type_size callback,
since that now safely falls back to the type_uint64 callback.

Then, in qapi-visit-core.c, we can now use the guaranteed
type_uint64 callback as the fallback for all smaller unsigned
int visits.

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

---
v9: hoist in part of 11/35, drop Marc-Andre's R-b
v8: no change
v7: split off int64 callbacks and retitle, add more FIXMEs in the
code, hoist use of type_uint64 here from 3/23, improved commit
message
v6: new patch, but stems from v5 23/46
---
 include/qapi/visitor-impl.h  |  9 ++++++---
 qapi/qapi-dealloc-visitor.c  | 12 ++++++------
 qapi/qapi-visit-core.c       | 42 ++++++++++++++----------------------------
 qapi/qmp-input-visitor.c     | 17 +++++++++++++++++
 qapi/qmp-output-visitor.c    |  9 +++++++++
 qapi/string-input-visitor.c  | 15 +++++++++++++++
 qapi/string-output-visitor.c |  9 +++++++++
 7 files changed, 76 insertions(+), 37 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index c263a26..45c1d3e 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -40,6 +40,12 @@ struct Visitor
     void (*type_int64)(Visitor *v, int64_t *obj, const char *name,
                        Error **errp);
     /* Must be set. */
+    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name,
+                        Error **errp);
+    /* Optional; fallback is type_uint64().  */
+    void (*type_size)(Visitor *v, uint64_t *obj, const char *name,
+                      Error **errp);
+    /* Must be set. */
     void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
@@ -53,12 +59,9 @@ struct Visitor
     void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
     void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
     void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
-    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
     void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
     void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
     void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
-    /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */
-    void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
     bool (*start_union)(Visitor *v, bool data_present, Error **errp);
     void (*end_union)(Visitor *v, bool data_present, Error **errp);
 };
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index e9b9f3f..11eb828 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -140,6 +140,11 @@ static void qapi_dealloc_type_int64(Visitor *v, int64_t *obj, const char *name,
 {
 }

+static void qapi_dealloc_type_uint64(Visitor *v, uint64_t *obj,
+                                     const char *name, Error **errp)
+{
+}
+
 static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name,
                                    Error **errp)
 {
@@ -158,11 +163,6 @@ static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
     }
 }

-static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
-                                   Error **errp)
-{
-}
-
 static void qapi_dealloc_type_enum(Visitor *v, int *obj,
                                    const char * const strings[],
                                    const char *kind, const char *name,
@@ -220,11 +220,11 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.end_list = qapi_dealloc_end_list;
     v->visitor.type_enum = qapi_dealloc_type_enum;
     v->visitor.type_int64 = qapi_dealloc_type_int64;
+    v->visitor.type_uint64 = qapi_dealloc_type_uint64;
     v->visitor.type_bool = qapi_dealloc_type_bool;
     v->visitor.type_str = qapi_dealloc_type_str;
     v->visitor.type_number = qapi_dealloc_type_number;
     v->visitor.type_any = qapi_dealloc_type_anything;
-    v->visitor.type_size = qapi_dealloc_type_size;
     v->visitor.start_union = qapi_dealloc_start_union;

     QTAILQ_INIT(&v->stack);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 6295fa8..4a8ad43 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -102,15 +102,15 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)

 void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
+    uint64_t value;

     if (v->type_uint8) {
         v->type_uint8(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int64(v, &value, name, errp);
-        if (value < 0 || value > UINT8_MAX) {
-            /* FIXME questionable reuse of errp if type_int64() changes
+        v->type_uint64(v, &value, name, errp);
+        if (value > UINT8_MAX) {
+            /* FIXME questionable reuse of errp if type_uint64() changes
                value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "uint8_t");
@@ -122,15 +122,15 @@ void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)

 void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
+    uint64_t value;

     if (v->type_uint16) {
         v->type_uint16(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int64(v, &value, name, errp);
-        if (value < 0 || value > UINT16_MAX) {
-            /* FIXME questionable reuse of errp if type_int64() changes
+        v->type_uint64(v, &value, name, errp);
+        if (value > UINT16_MAX) {
+            /* FIXME questionable reuse of errp if type_uint64() changes
                value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "uint16_t");
@@ -142,15 +142,15 @@ void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp

 void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
+    uint64_t value;

     if (v->type_uint32) {
         v->type_uint32(v, obj, name, errp);
     } else {
         value = *obj;
-        v->type_int64(v, &value, name, errp);
-        if (value < 0 || value > UINT32_MAX) {
-            /* FIXME questionable reuse of errp if type_int64() changes
+        v->type_uint64(v, &value, name, errp);
+        if (value > UINT32_MAX) {
+            /* FIXME questionable reuse of errp if type_uint64() changes
                value on error */
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
                        name ? name : "null", "uint32_t");
@@ -162,15 +162,7 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp

 void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
-
-    if (v->type_uint64) {
-        v->type_uint64(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_int64(v, &value, name, errp);
-        *obj = value;
-    }
+    v->type_uint64(v, obj, name, errp);
 }

 void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
@@ -240,16 +232,10 @@ void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)

 void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
-
     if (v->type_size) {
         v->type_size(v, obj, name, errp);
-    } else if (v->type_uint64) {
-        v->type_uint64(v, obj, name, errp);
     } else {
-        value = *obj;
-        v->type_int64(v, &value, name, errp);
-        *obj = value;
+        v->type_uint64(v, obj, name, errp);
     }
 }

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 0d8a3c3..32b60bb 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -239,6 +239,22 @@ static void qmp_input_type_int64(Visitor *v, int64_t *obj, const char *name,
     *obj = qint_get_int(qint);
 }

+static void qmp_input_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+                                  Error **errp)
+{
+    /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
+    QmpInputVisitor *qiv = to_qiv(v);
+    QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
+
+    if (!qint) {
+        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                   "integer");
+        return;
+    }
+
+    *obj = qint_get_int(qint);
+}
+
 static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
                                 Error **errp)
 {
@@ -342,6 +358,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.end_list = qmp_input_end_list;
     v->visitor.type_enum = input_type_enum;
     v->visitor.type_int64 = qmp_input_type_int64;
+    v->visitor.type_uint64 = qmp_input_type_uint64;
     v->visitor.type_bool = qmp_input_type_bool;
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 3984011..f8eebaa 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -165,6 +165,14 @@ static void qmp_output_type_int64(Visitor *v, int64_t *obj, const char *name,
     qmp_output_add(qov, name, qint_from_int(*obj));
 }

+static void qmp_output_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+                                   Error **errp)
+{
+    /* FIXME: QMP outputs values larger than INT64_MAX as negative */
+    QmpOutputVisitor *qov = to_qov(v);
+    qmp_output_add(qov, name, qint_from_int(*obj));
+}
+
 static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name,
                                  Error **errp)
 {
@@ -242,6 +250,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.end_list = qmp_output_end_list;
     v->visitor.type_enum = output_type_enum;
     v->visitor.type_int64 = qmp_output_type_int64;
+    v->visitor.type_uint64 = qmp_output_type_uint64;
     v->visitor.type_bool = qmp_output_type_bool;
     v->visitor.type_str = qmp_output_type_str;
     v->visitor.type_number = qmp_output_type_number;
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 2f422f0..d7546b5 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -226,6 +226,20 @@ error:
                "an int64 value or range");
 }

+static void parse_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+                              Error **errp)
+{
+    /* FIXME: parse_type_int64 mishandles values over INT64_MAX */
+    int64_t i;
+    Error *err = NULL;
+    parse_type_int64(v, &i, name, &err);
+    if (err) {
+        error_propagate(errp, err);
+    } else {
+        *obj = i;
+    }
+}
+
 static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
                             Error **errp)
 {
@@ -336,6 +350,7 @@ StringInputVisitor *string_input_visitor_new(const char *str)

     v->visitor.type_enum = input_type_enum;
     v->visitor.type_int64 = parse_type_int64;
+    v->visitor.type_uint64 = parse_type_uint64;
     v->visitor.type_size = parse_type_size;
     v->visitor.type_bool = parse_type_bool;
     v->visitor.type_str = parse_type_str;
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index c0a9331..3ed2b2c 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -197,6 +197,14 @@ static void print_type_int64(Visitor *v, int64_t *obj, const char *name,
     }
 }

+static void print_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+                             Error **errp)
+{
+    /* FIXME: print_type_int64 mishandles values over INT64_MAX */
+    int64_t i = *obj;
+    print_type_int64(v, &i, name, errp);
+}
+
 static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
                            Error **errp)
 {
@@ -346,6 +354,7 @@ StringOutputVisitor *string_output_visitor_new(bool human)
     v->human = human;
     v->visitor.type_enum = output_type_enum;
     v->visitor.type_int64 = print_type_int64;
+    v->visitor.type_uint64 = print_type_uint64;
     v->visitor.type_size = print_type_size;
     v->visitor.type_bool = print_type_bool;
     v->visitor.type_str = print_type_str;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (9 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 17:34   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int* Eric Blake
                   ` (26 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Commit 4e27e819 introduced optional visitor callbacks for all
sorts of int types, but no visitor has supplied any of the
callbacks for sizes less than 64 bits.  In other words, the
generic implementation based on using type_[u]int64() followed
by bounds-checking works just fine. In the interest of
simplicity, it's easier to make the visitor callback interface
not have to worry about the other sizes.

Adding some helper functions minimizes the boilerplate required
to correct FIXMEs added earlier with regards to questionable
reuse of errp, particularly now that we can guarantee from a
single file audit that value is unchanged if an error is set.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: hoist some of visitor-impl.h changes into 9/35 and 10/35
v8: no change
v7: further factor out helper functions that eliminate the
questionable errp reuse
v6: split off from v5 23/46
original version also appeared in v6-v9 of subset D
---
 include/qapi/visitor-impl.h |   8 +--
 qapi/qapi-visit-core.c      | 158 +++++++++++++++++---------------------------
 2 files changed, 60 insertions(+), 106 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 45c1d3e..e6399d1 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -1,7 +1,7 @@
 /*
  * Core Definitions for QAPI Visitor implementations
  *
- * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012, 2015-2016 Red Hat, Inc.
  *
  * Author: Paolo Bonizni <pbonzini@redhat.com>
  *
@@ -56,12 +56,6 @@ struct Visitor
     /* May be NULL; most useful for input visitors. */
     void (*optional)(Visitor *v, bool *present, const char *name);

-    void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
-    void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
-    void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
-    void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
-    void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
-    void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
     bool (*start_union)(Visitor *v, bool data_present, Error **errp);
     void (*end_union)(Visitor *v, bool data_present, Error **errp);
 };
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 4a8ad43..a48fd4e 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -100,129 +100,89 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     v->type_int64(v, obj, name, errp);
 }

+static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name,
+                             uint64_t max, const char *type, Error **errp)
+{
+    Error *err = NULL;
+    uint64_t value = *obj;
+
+    v->type_uint64(v, &value, name, &err);
+    if (err) {
+        error_propagate(errp, err);
+    } else if (value > max) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+                   name ? name : "null", type);
+    } else {
+        *obj = value;
+    }
+}
+
 void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
 {
-    uint64_t value;
-
-    if (v->type_uint8) {
-        v->type_uint8(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_uint64(v, &value, name, errp);
-        if (value > UINT8_MAX) {
-            /* FIXME questionable reuse of errp if type_uint64() changes
-               value on error */
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name ? name : "null", "uint8_t");
-            return;
-        }
-        *obj = value;
-    }
+    uint64_t value = *obj;
+    visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp);
+    *obj = value;
 }

-void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
+void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name,
+                       Error **errp)
 {
-    uint64_t value;
-
-    if (v->type_uint16) {
-        v->type_uint16(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_uint64(v, &value, name, errp);
-        if (value > UINT16_MAX) {
-            /* FIXME questionable reuse of errp if type_uint64() changes
-               value on error */
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name ? name : "null", "uint16_t");
-            return;
-        }
-        *obj = value;
-    }
+    uint64_t value = *obj;
+    visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp);
+    *obj = value;
 }

-void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
+void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name,
+                       Error **errp)
 {
-    uint64_t value;
-
-    if (v->type_uint32) {
-        v->type_uint32(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_uint64(v, &value, name, errp);
-        if (value > UINT32_MAX) {
-            /* FIXME questionable reuse of errp if type_uint64() changes
-               value on error */
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name ? name : "null", "uint32_t");
-            return;
-        }
-        *obj = value;
-    }
+    uint64_t value = *obj;
+    visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp);
+    *obj = value;
 }

-void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+                       Error **errp)
 {
     v->type_uint64(v, obj, name, errp);
 }

+static void visit_type_intN(Visitor *v, int64_t *obj, const char *name,
+                            int64_t min, int64_t max, const char *type,
+                            Error **errp)
+{
+    Error *err = NULL;
+    int64_t value = *obj;
+
+    v->type_int64(v, &value, name, &err);
+    if (err) {
+        error_propagate(errp, err);
+    } else if (value < min || value > max) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+                   name ? name : "null", type);
+    } else {
+        *obj = value;
+    }
+}
+
 void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
-
-    if (v->type_int8) {
-        v->type_int8(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_int64(v, &value, name, errp);
-        if (value < INT8_MIN || value > INT8_MAX) {
-            /* FIXME questionable reuse of errp if type_int64() changes
-               value on error */
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name ? name : "null", "int8_t");
-            return;
-        }
-        *obj = value;
-    }
+    int64_t value = *obj;
+    visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp);
+    *obj = value;
 }

 void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
-
-    if (v->type_int16) {
-        v->type_int16(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_int64(v, &value, name, errp);
-        if (value < INT16_MIN || value > INT16_MAX) {
-            /* FIXME questionable reuse of errp if type_int64() changes
-               value on error */
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name ? name : "null", "int16_t");
-            return;
-        }
-        *obj = value;
-    }
+    int64_t value = *obj;
+    visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", errp);
+    *obj = value;
 }

 void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
 {
-    int64_t value;
-
-    if (v->type_int32) {
-        v->type_int32(v, obj, name, errp);
-    } else {
-        value = *obj;
-        v->type_int64(v, &value, name, errp);
-        if (value < INT32_MIN || value > INT32_MAX) {
-            /* FIXME questionable reuse of errp if type_int64() changes
-               value on error */
-            error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
-                       name ? name : "null", "int32_t");
-            return;
-        }
-        *obj = value;
-    }
+    int64_t value = *obj;
+    visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", errp);
+    *obj = value;
 }

 void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int*
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (10 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 18:08   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 13/37] qom: Use typedef for Visitor Eric Blake
                   ` (25 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

C compilers are allowed to represent enums as a smaller type
than int, if all enum values fit in the smaller type.  There
are even compiler flags that force the use of this smaller
representation, and using them changes the ABI of a binary.
Therefore, our generated code for visit_type_ENUM() (for all
qapi enums) was wrong for casting Enum* to int* when calling
visit_type_enum().

It appears that no one has been doing this for qemu, because
if they had, we are potentially dereferencing beyond bounds
or even risking a SIGBUS on platforms where unaligned pointer
dereferencing is fatal.  Better is to avoid the practice
entirely, and just use the correct types.

This matches the fix for alternate qapi types, done earlier in
commit 0426d53 "qapi: Simplify visiting of alternate types",
with generated code changing as:

| void visit_type_GuestDiskBusType(Visitor *v, GuestDiskBusType *obj, const char *name, Error **errp)
| {
|-    visit_type_enum(v, (int *)obj, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
|+    int tmp = *obj;
|+    visit_type_enum(v, &tmp, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
|+    *obj = tmp;
| }

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: mention earlier commit id, enhance commit message
v8: no change
v7: rebase on typo fix
v6: new patch
---
 scripts/qapi-visit.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 4a4f67d..6bd188b 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -178,12 +178,13 @@ out:


 def gen_visit_enum(name):
-    # FIXME cast from enum *obj to int * invalidly assumes enum is int
     return mcgen('''

 void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
 {
-    visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
+    int tmp = *obj;
+    visit_type_enum(v, &tmp, %(c_name)s_lookup, "%(name)s", name, errp);
+    *obj = tmp;
 }
 ''',
                  c_name=c_name(name), name=name)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 13/37] qom: Use typedef for Visitor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (11 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int* Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement Eric Blake
                   ` (24 subsequent siblings)
  37 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eduardo Habkost, Michael S. Tsirkin, armbru, Paolo Bonzini,
	marcandre.lureau, Jiri Slaby, Andreas Färber,
	Richard Henderson

No need to repeat 'struct Visitor' when we already have it in
typedefs.h.  Omitting the redundant 'struct' also makes a later
patch easier to search for all object property callbacks that
are associated with a Visitor.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: new patch
---
 hw/misc/edu.c              | 4 ++--
 hw/virtio/virtio-balloon.c | 6 +++---
 include/qom/object.h       | 9 ++++-----
 qom/object.c               | 4 ++--
 target-i386/cpu.c          | 4 ++--
 5 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index 43d5b18..a7171eb 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -362,8 +362,8 @@ static void pci_edu_uninit(PCIDevice *pdev)
     timer_del(&edu->dma_timer);
 }

-static void edu_obj_uint64(Object *obj, struct Visitor *v, void *opaque,
-                const char *name, Error **errp)
+static void edu_obj_uint64(Object *obj, Visitor *v, void *opaque,
+                           const char *name, Error **errp)
 {
     uint64_t *val = opaque;

diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 1ce987a..9cc7cf9 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -110,7 +110,7 @@ static void balloon_stats_poll_cb(void *opaque)
     virtio_notify(vdev, s->svq);
 }

-static void balloon_stats_get_all(Object *obj, struct Visitor *v,
+static void balloon_stats_get_all(Object *obj, Visitor *v,
                                   void *opaque, const char *name, Error **errp)
 {
     Error *err = NULL;
@@ -149,7 +149,7 @@ out:
     error_propagate(errp, err);
 }

-static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
+static void balloon_stats_get_poll_interval(Object *obj, Visitor *v,
                                             void *opaque, const char *name,
                                             Error **errp)
 {
@@ -157,7 +157,7 @@ static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
     visit_type_int(v, &s->stats_poll_interval, name, errp);
 }

-static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v,
+static void balloon_stats_set_poll_interval(Object *obj, Visitor *v,
                                             void *opaque, const char *name,
                                             Error **errp)
 {
diff --git a/include/qom/object.h b/include/qom/object.h
index d0dafe9..3e7e99d 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -18,10 +18,9 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "qemu/queue.h"
+#include "qemu/typedefs.h"
 #include "qapi/error.h"

-struct Visitor;
-
 struct TypeImpl;
 typedef struct TypeImpl *Type;

@@ -298,7 +297,7 @@ typedef struct InterfaceInfo InterfaceInfo;
  * Called when trying to get/set a property.
  */
 typedef void (ObjectPropertyAccessor)(Object *obj,
-                                      struct Visitor *v,
+                                      Visitor *v,
                                       void *opaque,
                                       const char *name,
                                       Error **errp);
@@ -1025,7 +1024,7 @@ void object_unparent(Object *obj);
  *
  * Reads a property from a object.
  */
-void object_property_get(Object *obj, struct Visitor *v, const char *name,
+void object_property_get(Object *obj, Visitor *v, const char *name,
                          Error **errp);

 /**
@@ -1161,7 +1160,7 @@ void object_property_get_uint16List(Object *obj, const char *name,
  *
  * Writes a property to a object.
  */
-void object_property_set(Object *obj, struct Visitor *v, const char *name,
+void object_property_set(Object *obj, Visitor *v, const char *name,
                          Error **errp);

 /**
diff --git a/qom/object.c b/qom/object.c
index 5ff97ab..4d7d8c8 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -2184,7 +2184,7 @@ typedef struct {
     char *target_name;
 } AliasProperty;

-static void property_get_alias(Object *obj, struct Visitor *v, void *opaque,
+static void property_get_alias(Object *obj, Visitor *v, void *opaque,
                                const char *name, Error **errp)
 {
     AliasProperty *prop = opaque;
@@ -2192,7 +2192,7 @@ static void property_get_alias(Object *obj, struct Visitor *v, void *opaque,
     object_property_get(prop->target_obj, v, prop->target_name, errp);
 }

-static void property_set_alias(Object *obj, struct Visitor *v, void *opaque,
+static void property_set_alias(Object *obj, Visitor *v, void *opaque,
                                const char *name, Error **errp)
 {
     AliasProperty *prop = opaque;
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 0d447b5..13c5b69 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2921,7 +2921,7 @@ typedef struct BitProperty {
 } BitProperty;

 static void x86_cpu_get_bit_prop(Object *obj,
-                                 struct Visitor *v,
+                                 Visitor *v,
                                  void *opaque,
                                  const char *name,
                                  Error **errp)
@@ -2932,7 +2932,7 @@ static void x86_cpu_get_bit_prop(Object *obj,
 }

 static void x86_cpu_set_bit_prop(Object *obj,
-                                 struct Visitor *v,
+                                 Visitor *v,
                                  void *opaque,
                                  const char *name,
                                  Error **errp)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (12 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 13/37] qom: Use typedef for Visitor Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 18:28   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor Eric Blake
                   ` (23 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Alexander Graf, open list:Block layer core,
	Eduardo Habkost, Paolo Bonzini, Michael S. Tsirkin, Jiri Slaby,
	Jason Wang, armbru, Luiz Capitulino, Keith Busch, Gonglei,
	open list:sPAPR, Gerd Hoffmann, Igor Mammedov, marcandre.lureau,
	David Gibson, John Snow, Michael Roth, Andreas Färber,
	Richard Henderson

JSON uses "name":value, but many of our visitor interfaces were
called with visit_type_FOO(v, &value, name, errp).  This can be
a bit confusing to have to mentally swap the parameter order to
match JSON order.  It's particularly bad for visit_start_struct(),
where the 'name' parameter is smack in the middle of the
otherwise-related group of 'obj, kind, size' parameters! It's
time to do a global swap of the parameter ordering, so that the
'name' parameter is always immediately after the Visitor argument.

Additional reasons in favor of the swap: name is always an input
parameter, while &value is sometimes an output parameter (depending
on whether the caller is using an input visitor); and it is nicer
to list input parameters first.  Also, the existing include/qjson.h
prefers listing 'name' first in json_prop_*(), and I have plans to
unify that file with the qapi visitors; listing 'name' first in
qapi will minimize churn to the (admittedly few) qjson.h clients.

The next patches will then fix docs, object.h, visitor-impl.h, and
those clients to match.

Done by first patching scripts/qapi*.py by hand to make generated
files do what I want, then by running the following Coccinelle
script to affect the rest of the code base:
 $ spatch --sp-file script `git grep -l '\bvisit_' -- '**/*.[ch]'`
I then had to apply some touchups (Coccinelle insisted on TAB
indentation in visitor.h, and botched the signature of
visit_type_enum() by rewriting 'const char *const strings[]' to
the syntactically invalid 'const char*const[] strings').

    // Part 1: Swap declaration order
    @@
    type TV, TErr, TObj, T1, T2;
    identifier OBJ, ARG1, ARG2;
    @@
     void visit_start_struct
    -(TV v, TObj OBJ, T1 ARG1, const char *name, T2 ARG2, TErr errp)
    +(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
     { ... }

    @@
    type bool, TV, T1;
    identifier ARG1;
    @@
     bool visit_optional
    -(TV v, T1 ARG1, const char *name)
    +(TV v, const char *name, T1 ARG1)
     { ... }

    @@
    type TV, TErr, TObj, T1;
    identifier OBJ, ARG1;
    @@
     void visit_get_next_type
    -(TV v, TObj OBJ, T1 ARG1, const char *name, TErr errp)
    +(TV v, const char *name, TObj OBJ, T1 ARG1, TErr errp)
     { ... }

    @@
    type TV, TErr, TObj, T1, T2;
    identifier OBJ, ARG1, ARG2;
    @@
     void visit_type_enum
    -(TV v, TObj OBJ, T1 ARG1, T2 ARG2, const char *name, TErr errp)
    +(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
     { ... }

    @@
    type TV, TErr, TObj;
    identifier OBJ;
    identifier VISIT_TYPE =~ "^visit_type_";
    @@
     void VISIT_TYPE
    -(TV v, TObj OBJ, const char *name, TErr errp)
    +(TV v, const char *name, TObj OBJ, TErr errp)
     { ... }

    // Part 2: swap caller order
    @@
    expression V, NAME, OBJ, ARG1, ARG2, ERR;
    identifier VISIT_TYPE =~ "^visit_type_";
    @@
    (
    -visit_start_struct(V, OBJ, ARG1, NAME, ARG2, ERR)
    +visit_start_struct(V, NAME, OBJ, ARG1, ARG2, ERR)
    |
    -visit_optional(V, ARG1, NAME)
    +visit_optional(V, NAME, ARG1)
    |
    -visit_get_next_type(V, OBJ, ARG1, NAME, ERR)
    +visit_get_next_type(V, NAME, OBJ, ARG1, ERR)
    |
    -visit_type_enum(V, OBJ, ARG1, ARG2, NAME, ERR)
    +visit_type_enum(V, NAME, OBJ, ARG1, ARG2, ERR)
    |
    -VISIT_TYPE(V, OBJ, NAME, ERR)
    +VISIT_TYPE(V, NAME, OBJ, ERR)
    )

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: mention later docs cleanup in commit message, rebase to master
v8: new patch
---
 backends/hostmem.c                 |  8 ++--
 block/qapi.c                       |  2 +-
 blockdev.c                         |  4 +-
 bootdevice.c                       |  4 +-
 hmp.c                              |  8 ++--
 hw/acpi/core.c                     |  4 +-
 hw/acpi/ich9.c                     | 14 +++----
 hw/block/nvme.c                    |  4 +-
 hw/core/machine.c                  | 10 ++---
 hw/core/qdev-properties-system.c   | 12 +++---
 hw/core/qdev-properties.c          | 66 +++++++++++++++----------------
 hw/core/qdev.c                     |  2 +-
 hw/i386/pc.c                       | 14 +++----
 hw/ide/qdev.c                      |  4 +-
 hw/intc/xics.c                     |  8 ++--
 hw/isa/lpc_ich9.c                  |  2 +-
 hw/mem/pc-dimm.c                   |  2 +-
 hw/misc/edu.c                      |  2 +-
 hw/misc/tmp105.c                   |  4 +-
 hw/net/ne2000-isa.c                |  4 +-
 hw/pci-host/piix.c                 |  8 ++--
 hw/pci-host/q35.c                  | 10 ++---
 hw/ppc/spapr_drc.c                 | 12 +++---
 hw/usb/dev-storage.c               |  4 +-
 hw/virtio/virtio-balloon.c         | 12 +++---
 include/qapi/visitor.h             | 52 +++++++++++++++----------
 memory.c                           |  8 ++--
 net/dump.c                         |  4 +-
 net/filter-buffer.c                |  4 +-
 net/net.c                          |  4 +-
 numa.c                             |  6 +--
 qapi/qapi-visit-core.c             | 54 +++++++++++++------------
 qemu-img.c                         | 11 +++---
 qom/object.c                       | 49 ++++++++++++-----------
 replay/replay-input.c              |  4 +-
 scripts/qapi-commands.py           |  4 +-
 scripts/qapi-event.py              |  2 +-
 scripts/qapi-types.py              |  2 +-
 scripts/qapi-visit.py              | 28 ++++++-------
 scripts/qapi.py                    |  4 +-
 target-i386/cpu.c                  | 30 +++++++-------
 target-ppc/translate_init.c        |  4 +-
 tests/test-opts-visitor.c          |  6 +--
 tests/test-qdev-global-props.c     |  4 +-
 tests/test-qmp-commands.c          |  2 +-
 tests/test-qmp-input-strict.c      | 28 ++++++-------
 tests/test-qmp-input-visitor.c     | 80 +++++++++++++++++++-------------------
 tests/test-qmp-output-visitor.c    | 34 ++++++++--------
 tests/test-string-input-visitor.c  | 34 ++++++++--------
 tests/test-string-output-visitor.c | 16 ++++----
 tests/test-visitor-serialization.c | 54 ++++++++++++-------------
 util/qemu-sockets.c                |  4 +-
 vl.c                               |  4 +-
 53 files changed, 387 insertions(+), 373 deletions(-)

diff --git a/backends/hostmem.c b/backends/hostmem.c
index 1b4eb45..a9d30d8 100644
--- a/backends/hostmem.c
+++ b/backends/hostmem.c
@@ -32,7 +32,7 @@ host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque,
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
     uint64_t value = backend->size;

-    visit_type_size(v, &value, name, errp);
+    visit_type_size(v, name, &value, errp);
 }

 static void
@@ -48,7 +48,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque,
         goto out;
     }

-    visit_type_size(v, &value, name, &local_err);
+    visit_type_size(v, name, &value, &local_err);
     if (local_err) {
         goto out;
     }
@@ -91,7 +91,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque,
         node = &(*node)->next;
     } while (true);

-    visit_type_uint16List(v, &host_nodes, name, errp);
+    visit_type_uint16List(v, name, &host_nodes, errp);
 }

 static void
@@ -102,7 +102,7 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque,
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
     uint16List *l = NULL;

-    visit_type_uint16List(v, &l, name, errp);
+    visit_type_uint16List(v, name, &l, errp);

     while (l) {
         bitmap_set(backend->host_nodes, l->value, 1);
diff --git a/block/qapi.c b/block/qapi.c
index 58d3975..4fe3ce2 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -635,7 +635,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj, *data;

-    visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL,
+    visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec,
                                  &error_abort);
     obj = qmp_output_get_qobject(ov);
     assert(qobject_type(obj) == QTYPE_QDICT);
diff --git a/blockdev.c b/blockdev.c
index 1392fff..29f393c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3816,8 +3816,8 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
         }
     }

-    visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
-                               &options, NULL, &local_err);
+    visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options,
+                               &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto fail;
diff --git a/bootdevice.c b/bootdevice.c
index 3cdc0d7..d307cfc 100644
--- a/bootdevice.c
+++ b/bootdevice.c
@@ -274,7 +274,7 @@ static void device_get_bootindex(Object *obj, Visitor *v, void *opaque,
                                  const char *name, Error **errp)
 {
     BootIndexProperty *prop = opaque;
-    visit_type_int32(v, prop->bootindex, name, errp);
+    visit_type_int32(v, name, prop->bootindex, errp);
 }

 static void device_set_bootindex(Object *obj, Visitor *v, void *opaque,
@@ -284,7 +284,7 @@ static void device_set_bootindex(Object *obj, Visitor *v, void *opaque,
     int32_t boot_index;
     Error *local_err = NULL;

-    visit_type_int32(v, &boot_index, name, &local_err);
+    visit_type_int32(v, name, &boot_index, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/hmp.c b/hmp.c
index 6d67f9b..3beea3a 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1675,13 +1675,13 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
     }

     qdict_del(pdict, "qom-type");
-    visit_type_str(v, &type, "qom-type", &err);
+    visit_type_str(v, "qom-type", &type, &err);
     if (err) {
         goto out_end;
     }

     qdict_del(pdict, "id");
-    visit_type_str(v, &id, "id", &err);
+    visit_type_str(v, "id", &id, &err);
     if (err) {
         goto out_end;
     }
@@ -1949,8 +1949,8 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)

     while (m) {
         ov = string_output_visitor_new(false);
-        visit_type_uint16List(string_output_get_visitor(ov),
-                              &m->value->host_nodes, NULL, NULL);
+        visit_type_uint16List(string_output_get_visitor(ov), NULL,
+                              &m->value->host_nodes, NULL);
         monitor_printf(mon, "memory backend: %d\n", i);
         monitor_printf(mon, "  size:  %" PRId64 "\n", m->value->size);
         monitor_printf(mon, "  merge: %s\n",
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index 21e113d..ef1f037 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -242,7 +242,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
         OptsVisitor *ov;

         ov = opts_visitor_new(opts);
-        visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err);
+        visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err);
         opts_visitor_cleanup(ov);
     }

@@ -301,7 +301,7 @@ out:
         QapiDeallocVisitor *dv;

         dv = qapi_dealloc_visitor_new();
-        visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL,
+        visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), NULL, &hdrs,
                                     NULL);
         qapi_dealloc_visitor_cleanup(dv);
     }
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 1c7fcfa..0a486fb 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -289,7 +289,7 @@ static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v,
     ICH9LPCPMRegs *pm = opaque;
     uint32_t value = pm->pm_io_base + ICH9_PMIO_GPE0_STS;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp)
@@ -314,7 +314,7 @@ static void ich9_pm_get_disable_s3(Object *obj, Visitor *v,
     ICH9LPCPMRegs *pm = opaque;
     uint8_t value = pm->disable_s3;

-    visit_type_uint8(v, &value, name, errp);
+    visit_type_uint8(v, name, &value, errp);
 }

 static void ich9_pm_set_disable_s3(Object *obj, Visitor *v,
@@ -325,7 +325,7 @@ static void ich9_pm_set_disable_s3(Object *obj, Visitor *v,
     Error *local_err = NULL;
     uint8_t value;

-    visit_type_uint8(v, &value, name, &local_err);
+    visit_type_uint8(v, name, &value, &local_err);
     if (local_err) {
         goto out;
     }
@@ -341,7 +341,7 @@ static void ich9_pm_get_disable_s4(Object *obj, Visitor *v,
     ICH9LPCPMRegs *pm = opaque;
     uint8_t value = pm->disable_s4;

-    visit_type_uint8(v, &value, name, errp);
+    visit_type_uint8(v, name, &value, errp);
 }

 static void ich9_pm_set_disable_s4(Object *obj, Visitor *v,
@@ -352,7 +352,7 @@ static void ich9_pm_set_disable_s4(Object *obj, Visitor *v,
     Error *local_err = NULL;
     uint8_t value;

-    visit_type_uint8(v, &value, name, &local_err);
+    visit_type_uint8(v, name, &value, &local_err);
     if (local_err) {
         goto out;
     }
@@ -368,7 +368,7 @@ static void ich9_pm_get_s4_val(Object *obj, Visitor *v,
     ICH9LPCPMRegs *pm = opaque;
     uint8_t value = pm->s4_val;

-    visit_type_uint8(v, &value, name, errp);
+    visit_type_uint8(v, name, &value, errp);
 }

 static void ich9_pm_set_s4_val(Object *obj, Visitor *v,
@@ -379,7 +379,7 @@ static void ich9_pm_set_s4_val(Object *obj, Visitor *v,
     Error *local_err = NULL;
     uint8_t value;

-    visit_type_uint8(v, &value, name, &local_err);
+    visit_type_uint8(v, name, &value, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 169e4fa..3676e2e 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -920,7 +920,7 @@ static void nvme_get_bootindex(Object *obj, Visitor *v, void *opaque,
 {
     NvmeCtrl *s = NVME(obj);

-    visit_type_int32(v, &s->conf.bootindex, name, errp);
+    visit_type_int32(v, name, &s->conf.bootindex, errp);
 }

 static void nvme_set_bootindex(Object *obj, Visitor *v, void *opaque,
@@ -930,7 +930,7 @@ static void nvme_set_bootindex(Object *obj, Visitor *v, void *opaque,
     int32_t boot_index;
     Error *local_err = NULL;

-    visit_type_int32(v, &boot_index, name, &local_err);
+    visit_type_int32(v, name, &boot_index, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/hw/core/machine.c b/hw/core/machine.c
index c46ddc7..0fd1e73 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -40,7 +40,7 @@ static void machine_set_kernel_irqchip(Object *obj, Visitor *v,
     MachineState *ms = MACHINE(obj);
     OnOffSplit mode;

-    visit_type_OnOffSplit(v, &mode, name, &err);
+    visit_type_OnOffSplit(v, name, &mode, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -74,7 +74,7 @@ static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v,
     MachineState *ms = MACHINE(obj);
     int64_t value = ms->kvm_shadow_mem;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v,
@@ -85,7 +85,7 @@ static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v,
     Error *error = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &error);
+    visit_type_int(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
@@ -176,7 +176,7 @@ static void machine_get_phandle_start(Object *obj, Visitor *v,
     MachineState *ms = MACHINE(obj);
     int64_t value = ms->phandle_start;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void machine_set_phandle_start(Object *obj, Visitor *v,
@@ -187,7 +187,7 @@ static void machine_set_phandle_start(Object *obj, Visitor *v,
     Error *error = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &error);
+    visit_type_int(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 1589aba..86e0b02 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -30,7 +30,7 @@ static void get_pointer(Object *obj, Visitor *v, Property *prop,
     char *p;

     p = *ptr ? print(*ptr) : g_strdup("");
-    visit_type_str(v, &p, name, errp);
+    visit_type_str(v, name, &p, errp);
     g_free(p);
 }

@@ -50,7 +50,7 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop,
         return;
     }

-    visit_type_str(v, &str, name, &local_err);
+    visit_type_str(v, name, &str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -201,7 +201,7 @@ static void get_netdev(Object *obj, Visitor *v, void *opaque,
     NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
     char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");

-    visit_type_str(v, &p, name, errp);
+    visit_type_str(v, name, &p, errp);
     g_free(p);
 }

@@ -222,7 +222,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_str(v, &str, name, &local_err);
+    visit_type_str(v, name, &str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -307,7 +307,7 @@ static void get_vlan(Object *obj, Visitor *v, void *opaque,
         }
     }

-    visit_type_int32(v, &id, name, errp);
+    visit_type_int32(v, name, &id, errp);
 }

 static void set_vlan(Object *obj, Visitor *v, void *opaque,
@@ -326,7 +326,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_int32(v, &id, name, &local_err);
+    visit_type_int32(v, name, &id, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 3572810..971219d 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -48,8 +48,8 @@ static void get_enum(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     int *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_enum(v, ptr, prop->info->enum_table,
-                    prop->info->name, prop->name, errp);
+    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
+                    prop->info->name, errp);
 }

 static void set_enum(Object *obj, Visitor *v, void *opaque,
@@ -64,8 +64,8 @@ static void set_enum(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_enum(v, ptr, prop->info->enum_table,
-                    prop->info->name, prop->name, errp);
+    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
+                    prop->info->name, errp);
 }

 /* Bit */
@@ -95,7 +95,7 @@ static void prop_get_bit(Object *obj, Visitor *v, void *opaque,
     uint32_t *p = qdev_get_prop_ptr(dev, prop);
     bool value = (*p & qdev_get_prop_mask(prop)) != 0;

-    visit_type_bool(v, &value, name, errp);
+    visit_type_bool(v, name, &value, errp);
 }

 static void prop_set_bit(Object *obj, Visitor *v, void *opaque,
@@ -111,7 +111,7 @@ static void prop_set_bit(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_bool(v, &value, name, &local_err);
+    visit_type_bool(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -153,7 +153,7 @@ static void prop_get_bit64(Object *obj, Visitor *v, void *opaque,
     uint64_t *p = qdev_get_prop_ptr(dev, prop);
     bool value = (*p & qdev_get_prop_mask64(prop)) != 0;

-    visit_type_bool(v, &value, name, errp);
+    visit_type_bool(v, name, &value, errp);
 }

 static void prop_set_bit64(Object *obj, Visitor *v, void *opaque,
@@ -169,7 +169,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_bool(v, &value, name, &local_err);
+    visit_type_bool(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -193,7 +193,7 @@ static void get_bool(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     bool *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_bool(v, ptr, name, errp);
+    visit_type_bool(v, name, ptr, errp);
 }

 static void set_bool(Object *obj, Visitor *v, void *opaque,
@@ -208,7 +208,7 @@ static void set_bool(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_bool(v, ptr, name, errp);
+    visit_type_bool(v, name, ptr, errp);
 }

 PropertyInfo qdev_prop_bool = {
@@ -226,7 +226,7 @@ static void get_uint8(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     uint8_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_uint8(v, ptr, name, errp);
+    visit_type_uint8(v, name, ptr, errp);
 }

 static void set_uint8(Object *obj, Visitor *v, void *opaque,
@@ -241,7 +241,7 @@ static void set_uint8(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_uint8(v, ptr, name, errp);
+    visit_type_uint8(v, name, ptr, errp);
 }

 PropertyInfo qdev_prop_uint8 = {
@@ -259,7 +259,7 @@ static void get_uint16(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     uint16_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_uint16(v, ptr, name, errp);
+    visit_type_uint16(v, name, ptr, errp);
 }

 static void set_uint16(Object *obj, Visitor *v, void *opaque,
@@ -274,7 +274,7 @@ static void set_uint16(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_uint16(v, ptr, name, errp);
+    visit_type_uint16(v, name, ptr, errp);
 }

 PropertyInfo qdev_prop_uint16 = {
@@ -292,7 +292,7 @@ static void get_uint32(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     uint32_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_uint32(v, ptr, name, errp);
+    visit_type_uint32(v, name, ptr, errp);
 }

 static void set_uint32(Object *obj, Visitor *v, void *opaque,
@@ -307,7 +307,7 @@ static void set_uint32(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_uint32(v, ptr, name, errp);
+    visit_type_uint32(v, name, ptr, errp);
 }

 static void get_int32(Object *obj, Visitor *v, void *opaque,
@@ -317,7 +317,7 @@ static void get_int32(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     int32_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_int32(v, ptr, name, errp);
+    visit_type_int32(v, name, ptr, errp);
 }

 static void set_int32(Object *obj, Visitor *v, void *opaque,
@@ -332,7 +332,7 @@ static void set_int32(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_int32(v, ptr, name, errp);
+    visit_type_int32(v, name, ptr, errp);
 }

 PropertyInfo qdev_prop_uint32 = {
@@ -356,7 +356,7 @@ static void get_uint64(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     uint64_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_uint64(v, ptr, name, errp);
+    visit_type_uint64(v, name, ptr, errp);
 }

 static void set_uint64(Object *obj, Visitor *v, void *opaque,
@@ -371,7 +371,7 @@ static void set_uint64(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_uint64(v, ptr, name, errp);
+    visit_type_uint64(v, name, ptr, errp);
 }

 PropertyInfo qdev_prop_uint64 = {
@@ -397,9 +397,9 @@ static void get_string(Object *obj, Visitor *v, void *opaque,

     if (!*ptr) {
         char *str = (char *)"";
-        visit_type_str(v, &str, name, errp);
+        visit_type_str(v, name, &str, errp);
     } else {
-        visit_type_str(v, ptr, name, errp);
+        visit_type_str(v, name, ptr, errp);
     }
 }

@@ -417,7 +417,7 @@ static void set_string(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_str(v, &str, name, &local_err);
+    visit_type_str(v, name, &str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -460,7 +460,7 @@ static void get_mac(Object *obj, Visitor *v, void *opaque,
              mac->a[0], mac->a[1], mac->a[2],
              mac->a[3], mac->a[4], mac->a[5]);

-    visit_type_str(v, &p, name, errp);
+    visit_type_str(v, name, &p, errp);
 }

 static void set_mac(Object *obj, Visitor *v, void *opaque,
@@ -478,7 +478,7 @@ static void set_mac(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_str(v, &str, name, &local_err);
+    visit_type_str(v, name, &str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -561,11 +561,11 @@ static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_str(v, &str, name, &local_err);
+    visit_type_str(v, name, &str, &local_err);
     if (local_err) {
         error_free(local_err);
         local_err = NULL;
-        visit_type_int32(v, &value, name, &local_err);
+        visit_type_int32(v, name, &value, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
         } else if (value < -1 || value > 255) {
@@ -632,7 +632,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_uint16(v, &value, name, &local_err);
+    visit_type_uint16(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -678,7 +678,7 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
                   addr->domain, addr->bus, addr->slot, addr->function);
     assert(rc == sizeof(buffer) - 1);

-    visit_type_str(v, &p, name, errp);
+    visit_type_str(v, name, &p, errp);
 }

 /*
@@ -703,7 +703,7 @@ static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_str(v, &str, name, &local_err);
+    visit_type_str(v, name, &str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -825,7 +825,7 @@ static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
                    name);
         return;
     }
-    visit_type_uint32(v, alenptr, name, &local_err);
+    visit_type_uint32(v, name, alenptr, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1089,7 +1089,7 @@ static void get_size(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     uint64_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_size(v, ptr, name, errp);
+    visit_type_size(v, name, ptr, errp);
 }

 static void set_size(Object *obj, Visitor *v, void *opaque,
@@ -1099,7 +1099,7 @@ static void set_size(Object *obj, Visitor *v, void *opaque,
     Property *prop = opaque;
     uint64_t *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_size(v, ptr, name, errp);
+    visit_type_size(v, name, ptr, errp);
 }

 PropertyInfo qdev_prop_size = {
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 44bf790..b343a4d 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -897,7 +897,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
     char *ptr = buffer;

     prop->info->print(dev, prop, buffer, sizeof(buffer));
-    visit_type_str(v, &ptr, name, errp);
+    visit_type_str(v, name, &ptr, errp);
 }

 /**
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 9e37186..0ec39dc 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1760,7 +1760,7 @@ pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque,
     PCMachineState *pcms = PC_MACHINE(obj);
     int64_t value = memory_region_size(&pcms->hotplug_memory.mr);

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
@@ -1770,7 +1770,7 @@ static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
     PCMachineState *pcms = PC_MACHINE(obj);
     uint64_t value = pcms->max_ram_below_4g;

-    visit_type_size(v, &value, name, errp);
+    visit_type_size(v, name, &value, errp);
 }

 static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
@@ -1781,7 +1781,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
     Error *error = NULL;
     uint64_t value;

-    visit_type_size(v, &value, name, &error);
+    visit_type_size(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
@@ -1809,7 +1809,7 @@ static void pc_machine_get_vmport(Object *obj, Visitor *v, void *opaque,
     PCMachineState *pcms = PC_MACHINE(obj);
     OnOffAuto vmport = pcms->vmport;

-    visit_type_OnOffAuto(v, &vmport, name, errp);
+    visit_type_OnOffAuto(v, name, &vmport, errp);
 }

 static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque,
@@ -1817,7 +1817,7 @@ static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque,
 {
     PCMachineState *pcms = PC_MACHINE(obj);

-    visit_type_OnOffAuto(v, &pcms->vmport, name, errp);
+    visit_type_OnOffAuto(v, name, &pcms->vmport, errp);
 }

 bool pc_machine_is_smm_enabled(PCMachineState *pcms)
@@ -1851,7 +1851,7 @@ static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque,
     PCMachineState *pcms = PC_MACHINE(obj);
     OnOffAuto smm = pcms->smm;

-    visit_type_OnOffAuto(v, &smm, name, errp);
+    visit_type_OnOffAuto(v, name, &smm, errp);
 }

 static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
@@ -1859,7 +1859,7 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
 {
     PCMachineState *pcms = PC_MACHINE(obj);

-    visit_type_OnOffAuto(v, &pcms->smm, name, errp);
+    visit_type_OnOffAuto(v, name, &pcms->smm, errp);
 }

 static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 1f83109..66f56e7 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -204,7 +204,7 @@ static void ide_dev_get_bootindex(Object *obj, Visitor *v, void *opaque,
 {
     IDEDevice *d = IDE_DEVICE(obj);

-    visit_type_int32(v, &d->conf.bootindex, name, errp);
+    visit_type_int32(v, name, &d->conf.bootindex, errp);
 }

 static void ide_dev_set_bootindex(Object *obj, Visitor *v, void *opaque,
@@ -214,7 +214,7 @@ static void ide_dev_set_bootindex(Object *obj, Visitor *v, void *opaque,
     int32_t boot_index;
     Error *local_err = NULL;

-    visit_type_int32(v, &boot_index, name, &local_err);
+    visit_type_int32(v, name, &boot_index, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 9ff5796..502c87f 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -94,7 +94,7 @@ static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
     XICSState *icp = XICS_COMMON(obj);
     int64_t value = icp->nr_irqs;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
@@ -105,7 +105,7 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
     Error *error = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &error);
+    visit_type_int(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
@@ -128,7 +128,7 @@ static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
     XICSState *icp = XICS_COMMON(obj);
     int64_t value = icp->nr_servers;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
@@ -140,7 +140,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
     Error *error = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &error);
+    visit_type_int(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index ed9907d..358f707 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -576,7 +576,7 @@ static void ich9_lpc_get_sci_int(Object *obj, Visitor *v,
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj);
     uint32_t value = ich9_lpc_sci_irq(lpc);

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void ich9_lpc_add_properties(ICH9LPCState *lpc)
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index d5cdab2..123baeb 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -382,7 +382,7 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque,
     mr = host_memory_backend_get_memory(dimm->hostmem, errp);
     value = memory_region_size(mr);

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name,
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index a7171eb..4a3dc76 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -367,7 +367,7 @@ static void edu_obj_uint64(Object *obj, Visitor *v, void *opaque,
 {
     uint64_t *val = opaque;

-    visit_type_uint64(v, val, name, errp);
+    visit_type_uint64(v, name, val, errp);
 }

 static void edu_instance_init(Object *obj)
diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c
index f3fe8b8..1850315 100644
--- a/hw/misc/tmp105.c
+++ b/hw/misc/tmp105.c
@@ -58,7 +58,7 @@ static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
     TMP105State *s = TMP105(obj);
     int64_t value = s->temperature * 1000 / 256;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 /* Units are 0.001 centigrades relative to 0 C.  s->temperature is 8.8
@@ -71,7 +71,7 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque,
     Error *local_err = NULL;
     int64_t temp;

-    visit_type_int(v, &temp, name, &local_err);
+    visit_type_int(v, name, &temp, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 18b0644..46ef304 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -99,7 +99,7 @@ static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque,
     ISANE2000State *isa = ISA_NE2000(obj);
     NE2000State *s = &isa->ne2000;

-    visit_type_int32(v, &s->c.bootindex, name, errp);
+    visit_type_int32(v, name, &s->c.bootindex, errp);
 }

 static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque,
@@ -110,7 +110,7 @@ static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque,
     int32_t boot_index;
     Error *local_err = NULL;

-    visit_type_int32(v, &boot_index, name, &local_err);
+    visit_type_int32(v, name, &boot_index, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index b0d7e31..dbae0aa 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -221,7 +221,7 @@ static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v,
     I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
     uint32_t value = s->pci_info.w32.begin;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
@@ -231,7 +231,7 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
     I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
     uint32_t value = s->pci_info.w32.end;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
@@ -243,7 +243,7 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,

     pci_bus_get_w64_range(h->bus, &w64);

-    visit_type_uint64(v, &w64.begin, name, errp);
+    visit_type_uint64(v, name, &w64.begin, errp);
 }

 static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
@@ -255,7 +255,7 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,

     pci_bus_get_w64_range(h->bus, &w64);

-    visit_type_uint64(v, &w64.end, name, errp);
+    visit_type_uint64(v, name, &w64.end, errp);
 }

 static void i440fx_pcihost_initfn(Object *obj)
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 1fb4707..2b3608a 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -73,7 +73,7 @@ static void q35_host_get_pci_hole_start(Object *obj, Visitor *v,
     Q35PCIHost *s = Q35_HOST_DEVICE(obj);
     uint32_t value = s->mch.pci_info.w32.begin;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
@@ -83,7 +83,7 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
     Q35PCIHost *s = Q35_HOST_DEVICE(obj);
     uint32_t value = s->mch.pci_info.w32.end;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
@@ -95,7 +95,7 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,

     pci_bus_get_w64_range(h->bus, &w64);

-    visit_type_uint64(v, &w64.begin, name, errp);
+    visit_type_uint64(v, name, &w64.begin, errp);
 }

 static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
@@ -107,7 +107,7 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,

     pci_bus_get_w64_range(h->bus, &w64);

-    visit_type_uint64(v, &w64.end, name, errp);
+    visit_type_uint64(v, name, &w64.end, errp);
 }

 static void q35_host_get_mmcfg_size(Object *obj, Visitor *v,
@@ -117,7 +117,7 @@ static void q35_host_get_mmcfg_size(Object *obj, Visitor *v,
     PCIExpressHost *e = PCIE_HOST_BRIDGE(obj);
     uint32_t value = e->size;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static Property mch_props[] = {
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index dccb908..9c8fbe8 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -220,7 +220,7 @@ static void prop_get_index(Object *obj, Visitor *v, void *opaque,
     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
     uint32_t value = (uint32_t)drck->get_index(drc);
-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void prop_get_type(Object *obj, Visitor *v, void *opaque,
@@ -229,7 +229,7 @@ static void prop_get_type(Object *obj, Visitor *v, void *opaque,
     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
     uint32_t value = (uint32_t)drck->get_type(drc);
-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static char *prop_get_name(Object *obj, Error **errp)
@@ -247,7 +247,7 @@ static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
     uint32_t value;

     drck->entity_sense(drc, &value);
-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
@@ -259,7 +259,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
     void *fdt;

     if (!drc->fdt) {
-        visit_start_struct(v, NULL, NULL, name, 0, &err);
+        visit_start_struct(v, name, NULL, NULL, 0, &err);
         if (!err) {
             visit_end_struct(v, &err);
         }
@@ -282,7 +282,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
         case FDT_BEGIN_NODE:
             fdt_depth++;
             name = fdt_get_name(fdt, fdt_offset, &name_len);
-            visit_start_struct(v, NULL, NULL, name, 0, &err);
+            visit_start_struct(v, name, NULL, NULL, 0, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
@@ -308,7 +308,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
                 return;
             }
             for (i = 0; i < prop_len; i++) {
-                visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, &err);
+                visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err);
                 if (err) {
                     error_propagate(errp, err);
                     return;
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 597d8fd..3261446 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -786,7 +786,7 @@ static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque,
     USBDevice *dev = USB_DEVICE(obj);
     MSDState *s = USB_STORAGE_DEV(dev);

-    visit_type_int32(v, &s->conf.bootindex, name, errp);
+    visit_type_int32(v, name, &s->conf.bootindex, errp);
 }

 static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque,
@@ -797,7 +797,7 @@ static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque,
     int32_t boot_index;
     Error *local_err = NULL;

-    visit_type_int32(v, &boot_index, name, &local_err);
+    visit_type_int32(v, name, &boot_index, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 9cc7cf9..1d81a34 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -117,21 +117,21 @@ static void balloon_stats_get_all(Object *obj, Visitor *v,
     VirtIOBalloon *s = opaque;
     int i;

-    visit_start_struct(v, NULL, "guest-stats", name, 0, &err);
+    visit_start_struct(v, name, NULL, "guest-stats", 0, &err);
     if (err) {
         goto out;
     }
-    visit_type_int(v, &s->stats_last_update, "last-update", &err);
+    visit_type_int(v, "last-update", &s->stats_last_update, &err);
     if (err) {
         goto out_end;
     }

-    visit_start_struct(v, NULL, NULL, "stats", 0, &err);
+    visit_start_struct(v, "stats", NULL, NULL, 0, &err);
     if (err) {
         goto out_end;
     }
     for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
-        visit_type_uint64(v, &s->stats[i], balloon_stat_names[i], &err);
+        visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err);
         if (err) {
             goto out_nested;
         }
@@ -154,7 +154,7 @@ static void balloon_stats_get_poll_interval(Object *obj, Visitor *v,
                                             Error **errp)
 {
     VirtIOBalloon *s = opaque;
-    visit_type_int(v, &s->stats_poll_interval, name, errp);
+    visit_type_int(v, name, &s->stats_poll_interval, errp);
 }

 static void balloon_stats_set_poll_interval(Object *obj, Visitor *v,
@@ -165,7 +165,7 @@ static void balloon_stats_set_poll_interval(Object *obj, Visitor *v,
     Error *local_err = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &local_err);
+    visit_type_int(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index a14a16d..eb50116 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -27,8 +27,8 @@ typedef struct GenericList
     struct GenericList *next;
 } GenericList;

-void visit_start_struct(Visitor *v, void **obj, const char *kind,
-                        const char *name, size_t size, Error **errp);
+void visit_start_struct(Visitor *v, const char *name, void **obj,
+                        const char *kind, size_t size, Error **errp);
 void visit_end_struct(Visitor *v, Error **errp);
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
@@ -43,7 +43,7 @@ void visit_end_list(Visitor *v, Error **errp);
  * corresponding visit_type_*() needs calling; for other visitors,
  * leave *@present unchanged.  Return *@present for convenience.
  */
-bool visit_optional(Visitor *v, bool *present, const char *name);
+bool visit_optional(Visitor *v, const char *name, bool *present);

 /**
  * Determine the qtype of the item @name in the current object visit.
@@ -51,24 +51,34 @@ bool visit_optional(Visitor *v, bool *present, const char *name);
  * alternate type; for other visitors, leave *@type unchanged.
  * If @promote_int, treat integers as QTYPE_FLOAT.
  */
-void visit_get_next_type(Visitor *v, QType *type, bool promote_int,
-                         const char *name, Error **errp);
-void visit_type_enum(Visitor *v, int *obj, const char * const strings[],
-                     const char *kind, const char *name, Error **errp);
-void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
-void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp);
-void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp);
-void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp);
-void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp);
-void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp);
-void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp);
-void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp);
-void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp);
-void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
-void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
-void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
-void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
-void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp);
+void visit_get_next_type(Visitor *v, const char *name, QType *type,
+                         bool promote_int, Error **errp);
+void visit_type_enum(Visitor *v, const char *name, int *obj,
+                     const char *const strings[], const char *kind,
+                     Error **errp);
+void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
+void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
+                      Error **errp);
+void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj,
+                       Error **errp);
+void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
+                       Error **errp);
+void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
+                       Error **errp);
+void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp);
+void visit_type_int16(Visitor *v, const char *name, int16_t *obj,
+                      Error **errp);
+void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
+                      Error **errp);
+void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
+                      Error **errp);
+void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
+                     Error **errp);
+void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
+void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
+void visit_type_number(Visitor *v, const char *name, double *obj,
+                       Error **errp);
+void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
 bool visit_start_union(Visitor *v, bool data_present, Error **errp);
 void visit_end_union(Visitor *v, bool data_present, Error **errp);

diff --git a/memory.c b/memory.c
index 93bd8ed..49eb809 100644
--- a/memory.c
+++ b/memory.c
@@ -934,7 +934,7 @@ static void memory_region_get_addr(Object *obj, Visitor *v, void *opaque,
     MemoryRegion *mr = MEMORY_REGION(obj);
     uint64_t value = mr->addr;

-    visit_type_uint64(v, &value, name, errp);
+    visit_type_uint64(v, name, &value, errp);
 }

 static void memory_region_get_container(Object *obj, Visitor *v, void *opaque,
@@ -946,7 +946,7 @@ static void memory_region_get_container(Object *obj, Visitor *v, void *opaque,
     if (mr->container) {
         path = object_get_canonical_path(OBJECT(mr->container));
     }
-    visit_type_str(v, &path, name, errp);
+    visit_type_str(v, name, &path, errp);
     if (mr->container) {
         g_free(path);
     }
@@ -966,7 +966,7 @@ static void memory_region_get_priority(Object *obj, Visitor *v, void *opaque,
     MemoryRegion *mr = MEMORY_REGION(obj);
     int32_t value = mr->priority;

-    visit_type_int32(v, &value, name, errp);
+    visit_type_int32(v, name, &value, errp);
 }

 static bool memory_region_get_may_overlap(Object *obj, Error **errp)
@@ -982,7 +982,7 @@ static void memory_region_get_size(Object *obj, Visitor *v, void *opaque,
     MemoryRegion *mr = MEMORY_REGION(obj);
     uint64_t value = memory_region_size(mr);

-    visit_type_uint64(v, &value, name, errp);
+    visit_type_uint64(v, name, &value, errp);
 }

 static void memory_region_initfn(Object *obj)
diff --git a/net/dump.c b/net/dump.c
index 88d9582..0aac9b8 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -277,7 +277,7 @@ static void filter_dump_get_maxlen(Object *obj, Visitor *v, void *opaque,
     NetFilterDumpState *nfds = FILTER_DUMP(obj);
     uint32_t value = nfds->maxlen;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void filter_dump_set_maxlen(Object *obj, Visitor *v, void *opaque,
@@ -287,7 +287,7 @@ static void filter_dump_set_maxlen(Object *obj, Visitor *v, void *opaque,
     Error *local_err = NULL;
     uint32_t value;

-    visit_type_uint32(v, &value, name, &local_err);
+    visit_type_uint32(v, name, &value, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/net/filter-buffer.c b/net/filter-buffer.c
index 57be149..3173d26 100644
--- a/net/filter-buffer.c
+++ b/net/filter-buffer.c
@@ -138,7 +138,7 @@ static void filter_buffer_get_interval(Object *obj, Visitor *v, void *opaque,
     FilterBufferState *s = FILTER_BUFFER(obj);
     uint32_t value = s->interval;

-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void filter_buffer_set_interval(Object *obj, Visitor *v, void *opaque,
@@ -148,7 +148,7 @@ static void filter_buffer_set_interval(Object *obj, Visitor *v, void *opaque,
     Error *local_err = NULL;
     uint32_t value;

-    visit_type_uint32(v, &value, name, &local_err);
+    visit_type_uint32(v, name, &value, &local_err);
     if (local_err) {
         goto out;
     }
diff --git a/net/net.c b/net/net.c
index 87dd356..428636a 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1035,9 +1035,9 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
 static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp)
 {
     if (is_netdev) {
-        visit_type_Netdev(v, (Netdev **)object, NULL, errp);
+        visit_type_Netdev(v, NULL, (Netdev **)object, errp);
     } else {
-        visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp);
+        visit_type_NetLegacy(v, NULL, (NetLegacy **)object, errp);
     }
 }

diff --git a/numa.c b/numa.c
index 425ef8d..dbf3278 100644
--- a/numa.c
+++ b/numa.c
@@ -218,7 +218,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)

     {
         OptsVisitor *ov = opts_visitor_new(opts);
-        visit_type_NumaOptions(opts_get_visitor(ov), &object, NULL, &err);
+        visit_type_NumaOptions(opts_get_visitor(ov), NULL, &object, &err);
         opts_visitor_cleanup(ov);
     }

@@ -245,8 +245,8 @@ error:

     if (object) {
         QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
-        visit_type_NumaOptions(qapi_dealloc_get_visitor(dv),
-                               &object, NULL, NULL);
+        visit_type_NumaOptions(qapi_dealloc_get_visitor(dv), NULL, &object,
+                               NULL);
         qapi_dealloc_visitor_cleanup(dv);
     }

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index a48fd4e..609db22 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -17,8 +17,8 @@
 #include "qapi/visitor.h"
 #include "qapi/visitor-impl.h"

-void visit_start_struct(Visitor *v, void **obj, const char *kind,
-                        const char *name, size_t size, Error **errp)
+void visit_start_struct(Visitor *v, const char *name, void **obj,
+                        const char *kind, size_t size, Error **errp)
 {
     v->start_struct(v, obj, kind, name, size, errp);
 }
@@ -73,7 +73,7 @@ void visit_end_union(Visitor *v, bool data_present, Error **errp)
     }
 }

-bool visit_optional(Visitor *v, bool *present, const char *name)
+bool visit_optional(Visitor *v, const char *name, bool *present)
 {
     if (v->optional) {
         v->optional(v, present, name);
@@ -81,21 +81,22 @@ bool visit_optional(Visitor *v, bool *present, const char *name)
     return *present;
 }

-void visit_get_next_type(Visitor *v, QType *type, bool promote_int,
-                         const char *name, Error **errp)
+void visit_get_next_type(Visitor *v, const char *name, QType *type,
+                         bool promote_int, Error **errp)
 {
     if (v->get_next_type) {
         v->get_next_type(v, type, promote_int, name, errp);
     }
 }

-void visit_type_enum(Visitor *v, int *obj, const char * const strings[],
-                     const char *kind, const char *name, Error **errp)
+void visit_type_enum(Visitor *v, const char *name, int *obj,
+                     const char *const strings[], const char *kind,
+                     Error **errp)
 {
     v->type_enum(v, obj, strings, kind, name, errp);
 }

-void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
+void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
 {
     v->type_int64(v, obj, name, errp);
 }
@@ -117,14 +118,15 @@ static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name,
     }
 }

-void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
+void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
+                      Error **errp)
 {
     uint64_t value = *obj;
     visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp);
     *obj = value;
 }

-void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name,
+void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj,
                        Error **errp)
 {
     uint64_t value = *obj;
@@ -132,7 +134,7 @@ void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name,
     *obj = value;
 }

-void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name,
+void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
                        Error **errp)
 {
     uint64_t value = *obj;
@@ -140,7 +142,7 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name,
     *obj = value;
 }

-void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                        Error **errp)
 {
     v->type_uint64(v, obj, name, errp);
@@ -164,33 +166,37 @@ static void visit_type_intN(Visitor *v, int64_t *obj, const char *name,
     }
 }

-void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
+void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp)
 {
     int64_t value = *obj;
     visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp);
     *obj = value;
 }

-void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
+void visit_type_int16(Visitor *v, const char *name, int16_t *obj,
+                      Error **errp)
 {
     int64_t value = *obj;
     visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", errp);
     *obj = value;
 }

-void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
+void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
+                      Error **errp)
 {
     int64_t value = *obj;
     visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", errp);
     *obj = value;
 }

-void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
+void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
+                      Error **errp)
 {
     v->type_int64(v, obj, name, errp);
 }

-void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
+                     Error **errp)
 {
     if (v->type_size) {
         v->type_size(v, obj, name, errp);
@@ -199,23 +205,23 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     }
 }

-void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
+void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
 {
     v->type_bool(v, obj, name, errp);
 }

-void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
+void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
     v->type_str(v, obj, name, errp);
 }

-void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
+void visit_type_number(Visitor *v, const char *name, double *obj,
+                       Error **errp)
 {
     v->type_number(v, obj, name, errp);
 }

-void visit_type_any(Visitor *v, QObject **obj, const char *name,
-                    Error **errp)
+void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 {
     v->type_any(v, obj, name, errp);
 }
@@ -236,7 +242,7 @@ void output_type_enum(Visitor *v, int *obj, const char * const strings[],
     }

     enum_str = (char *)strings[value];
-    visit_type_str(v, &enum_str, name, errp);
+    visit_type_str(v, name, &enum_str, errp);
 }

 void input_type_enum(Visitor *v, int *obj, const char * const strings[],
@@ -249,7 +255,7 @@ void input_type_enum(Visitor *v, int *obj, const char * const strings[],

     assert(strings);

-    visit_type_str(v, &enum_str, name, &local_err);
+    visit_type_str(v, name, &enum_str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/qemu-img.c b/qemu-img.c
index a5949e6..5ee7a38 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -376,8 +376,8 @@ static void dump_json_image_check(ImageCheck *check, bool quiet)
     QString *str;
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageCheck(qmp_output_get_visitor(ov),
-                          &check, NULL, &local_err);
+    visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check,
+                          &local_err);
     obj = qmp_output_get_qobject(ov);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
@@ -1924,8 +1924,8 @@ static void dump_json_image_info_list(ImageInfoList *list)
     QString *str;
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfoList(qmp_output_get_visitor(ov),
-                             &list, NULL, &local_err);
+    visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list,
+                             &local_err);
     obj = qmp_output_get_qobject(ov);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
@@ -1941,8 +1941,7 @@ static void dump_json_image_info(ImageInfo *info)
     QString *str;
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfo(qmp_output_get_visitor(ov),
-                         &info, NULL, &local_err);
+    visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info, &local_err);
     obj = qmp_output_get_qobject(ov);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
diff --git a/qom/object.c b/qom/object.c
index 4d7d8c8..6a5ad59 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1243,8 +1243,8 @@ int object_property_get_enum(Object *obj, const char *name,
     str = string_output_get_string(sov);
     siv = string_input_visitor_new(str);
     string_output_visitor_cleanup(sov);
-    visit_type_enum(string_input_get_visitor(siv),
-                    &ret, enumprop->strings, NULL, name, errp);
+    visit_type_enum(string_input_get_visitor(siv), name, &ret,
+                    enumprop->strings, NULL, errp);

     g_free(str);
     string_input_visitor_cleanup(siv);
@@ -1269,8 +1269,7 @@ void object_property_get_uint16List(Object *obj, const char *name,
     }
     str = string_output_get_string(ov);
     iv = string_input_visitor_new(str);
-    visit_type_uint16List(string_input_get_visitor(iv),
-                          list, NULL, errp);
+    visit_type_uint16List(string_input_get_visitor(iv), NULL, list, errp);

     g_free(str);
     string_input_visitor_cleanup(iv);
@@ -1342,7 +1341,7 @@ static void object_get_child_property(Object *obj, Visitor *v, void *opaque,
     gchar *path;

     path = object_get_canonical_path(child);
-    visit_type_str(v, &path, name, errp);
+    visit_type_str(v, name, &path, errp);
     g_free(path);
 }

@@ -1413,11 +1412,11 @@ static void object_get_link_property(Object *obj, Visitor *v, void *opaque,

     if (*child) {
         path = object_get_canonical_path(*child);
-        visit_type_str(v, &path, name, errp);
+        visit_type_str(v, name, &path, errp);
         g_free(path);
     } else {
         path = (gchar *)"";
-        visit_type_str(v, &path, name, errp);
+        visit_type_str(v, name, &path, errp);
     }
 }

@@ -1471,7 +1470,7 @@ static void object_set_link_property(Object *obj, Visitor *v, void *opaque,
     Object *new_target = NULL;
     char *path = NULL;

-    visit_type_str(v, &path, name, &local_err);
+    visit_type_str(v, name, &path, &local_err);

     if (!local_err && strcmp(path, "") != 0) {
         new_target = object_resolve_link(obj, name, path, &local_err);
@@ -1738,7 +1737,7 @@ static void property_get_str(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_str(v, &value, name, errp);
+    visit_type_str(v, name, &value, errp);
     g_free(value);
 }

@@ -1749,7 +1748,7 @@ static void property_set_str(Object *obj, Visitor *v, void *opaque,
     char *value;
     Error *local_err = NULL;

-    visit_type_str(v, &value, name, &local_err);
+    visit_type_str(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1830,7 +1829,7 @@ static void property_get_bool(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_bool(v, &value, name, errp);
+    visit_type_bool(v, name, &value, errp);
 }

 static void property_set_bool(Object *obj, Visitor *v, void *opaque,
@@ -1840,7 +1839,7 @@ static void property_set_bool(Object *obj, Visitor *v, void *opaque,
     bool value;
     Error *local_err = NULL;

-    visit_type_bool(v, &value, name, &local_err);
+    visit_type_bool(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1913,7 +1912,7 @@ static void property_get_enum(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_enum(v, &value, prop->strings, NULL, name, errp);
+    visit_type_enum(v, name, &value, prop->strings, NULL, errp);
 }

 static void property_set_enum(Object *obj, Visitor *v, void *opaque,
@@ -1923,7 +1922,7 @@ static void property_set_enum(Object *obj, Visitor *v, void *opaque,
     int value;
     Error *err = NULL;

-    visit_type_enum(v, &value, prop->strings, NULL, name, &err);
+    visit_type_enum(v, name, &value, prop->strings, NULL, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -2004,31 +2003,31 @@ static void property_get_tm(Object *obj, Visitor *v, void *opaque,
         goto out;
     }

-    visit_start_struct(v, NULL, "struct tm", name, 0, &err);
+    visit_start_struct(v, name, NULL, "struct tm", 0, &err);
     if (err) {
         goto out;
     }
-    visit_type_int32(v, &value.tm_year, "tm_year", &err);
+    visit_type_int32(v, "tm_year", &value.tm_year, &err);
     if (err) {
         goto out_end;
     }
-    visit_type_int32(v, &value.tm_mon, "tm_mon", &err);
+    visit_type_int32(v, "tm_mon", &value.tm_mon, &err);
     if (err) {
         goto out_end;
     }
-    visit_type_int32(v, &value.tm_mday, "tm_mday", &err);
+    visit_type_int32(v, "tm_mday", &value.tm_mday, &err);
     if (err) {
         goto out_end;
     }
-    visit_type_int32(v, &value.tm_hour, "tm_hour", &err);
+    visit_type_int32(v, "tm_hour", &value.tm_hour, &err);
     if (err) {
         goto out_end;
     }
-    visit_type_int32(v, &value.tm_min, "tm_min", &err);
+    visit_type_int32(v, "tm_min", &value.tm_min, &err);
     if (err) {
         goto out_end;
     }
-    visit_type_int32(v, &value.tm_sec, "tm_sec", &err);
+    visit_type_int32(v, "tm_sec", &value.tm_sec, &err);
     if (err) {
         goto out_end;
     }
@@ -2096,7 +2095,7 @@ static void property_get_uint8_ptr(Object *obj, Visitor *v,
                                    Error **errp)
 {
     uint8_t value = *(uint8_t *)opaque;
-    visit_type_uint8(v, &value, name, errp);
+    visit_type_uint8(v, name, &value, errp);
 }

 static void property_get_uint16_ptr(Object *obj, Visitor *v,
@@ -2104,7 +2103,7 @@ static void property_get_uint16_ptr(Object *obj, Visitor *v,
                                    Error **errp)
 {
     uint16_t value = *(uint16_t *)opaque;
-    visit_type_uint16(v, &value, name, errp);
+    visit_type_uint16(v, name, &value, errp);
 }

 static void property_get_uint32_ptr(Object *obj, Visitor *v,
@@ -2112,7 +2111,7 @@ static void property_get_uint32_ptr(Object *obj, Visitor *v,
                                    Error **errp)
 {
     uint32_t value = *(uint32_t *)opaque;
-    visit_type_uint32(v, &value, name, errp);
+    visit_type_uint32(v, name, &value, errp);
 }

 static void property_get_uint64_ptr(Object *obj, Visitor *v,
@@ -2120,7 +2119,7 @@ static void property_get_uint64_ptr(Object *obj, Visitor *v,
                                    Error **errp)
 {
     uint64_t value = *(uint64_t *)opaque;
-    visit_type_uint64(v, &value, name, errp);
+    visit_type_uint64(v, name, &value, errp);
 }

 void object_property_add_uint8_ptr(Object *obj, const char *name,
diff --git a/replay/replay-input.c b/replay/replay-input.c
index bc15f59..ade61e4 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -28,7 +28,7 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)

     qov = qmp_output_visitor_new();
     ov = qmp_output_get_visitor(qov);
-    visit_type_InputEvent(ov, &src, NULL, &error_abort);
+    visit_type_InputEvent(ov, NULL, &src, &error_abort);
     obj = qmp_output_get_qobject(qov);
     qmp_output_visitor_cleanup(qov);
     if (!obj) {
@@ -37,7 +37,7 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)

     qiv = qmp_input_visitor_new(obj);
     iv = qmp_input_get_visitor(qiv);
-    visit_type_InputEvent(iv, &dst, NULL, &error_abort);
+    visit_type_InputEvent(iv, NULL, &dst, &error_abort);
     qmp_input_visitor_cleanup(qiv);
     qobject_decref(obj);

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 561e47a..91c5a4e 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -131,7 +131,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out,
     Visitor *v;

     v = qmp_output_get_visitor(qov);
-    visit_type_%(c_name)s(v, &ret_in, "unused", &err);
+    visit_type_%(c_name)s(v, "unused", &ret_in, &err);
     if (err) {
         goto out;
     }
@@ -142,7 +142,7 @@ out:
     qmp_output_visitor_cleanup(qov);
     qdv = qapi_dealloc_visitor_new();
     v = qapi_dealloc_get_visitor(qdv);
-    visit_type_%(c_name)s(v, &ret_in, "unused", NULL);
+    visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
     qapi_dealloc_visitor_cleanup(qdv);
 }
 ''',
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 761cda9..fd03e38 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -63,7 +63,7 @@ def gen_event_send(name, arg_type):
     qov = qmp_output_visitor_new();
     v = qmp_output_get_visitor(qov);

-    visit_start_struct(v, NULL, NULL, "%(name)s", 0, &err);
+    visit_start_struct(v, "%(name)s", NULL, NULL, 0, &err);
 ''',
                      name=name)
         ret += gen_err_check()
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 0d86269..d3f631a 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -155,7 +155,7 @@ void qapi_free_%(c_name)s(%(c_name)s *obj)

     qdv = qapi_dealloc_visitor_new();
     v = qapi_dealloc_get_visitor(qdv);
-    visit_type_%(c_name)s(v, &obj, NULL, NULL);
+    visit_type_%(c_name)s(v, NULL, &obj, NULL);
     qapi_dealloc_visitor_cleanup(qdv);
 }
 ''',
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 6bd188b..ca28b08 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -29,7 +29,7 @@ def gen_visit_decl(name, scalar=False):
     if not scalar:
         c_type += '*'
     return mcgen('''
-void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, Error **errp);
+void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
 ''',
                  c_name=c_name(name), c_type=c_type)

@@ -118,11 +118,11 @@ def gen_visit_struct(name, base, members):
     # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
     ret += mcgen('''

-void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;

-    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
+    visit_start_struct(v, name, (void **)obj, "%(name)s", sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
@@ -150,7 +150,7 @@ def gen_visit_list(name, element_type):
     # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
     return mcgen('''

-void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
     GenericList *i, **prev;
@@ -164,7 +164,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
          !err && (i = visit_next_list(v, prev, &err)) != NULL;
          prev = &i) {
         %(c_name)s *native_i = (%(c_name)s *)i;
-        visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err);
+        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
     }

     error_propagate(errp, err);
@@ -180,10 +180,10 @@ out:
 def gen_visit_enum(name):
     return mcgen('''

-void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
 {
     int tmp = *obj;
-    visit_type_enum(v, &tmp, %(c_name)s_lookup, "%(name)s", name, errp);
+    visit_type_enum(v, name, &tmp, %(c_name)s_lookup, "%(name)s", errp);
     *obj = tmp;
 }
 ''',
@@ -198,7 +198,7 @@ def gen_visit_alternate(name, variants):

     ret = mcgen('''

-void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;

@@ -206,7 +206,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
     if (err) {
         goto out;
     }
-    visit_get_next_type(v, &(*obj)->type, %(promote_int)s, name, &err);
+    visit_get_next_type(v, name, &(*obj)->type, %(promote_int)s, &err);
     if (err) {
         goto out_obj;
     }
@@ -217,7 +217,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
     for var in variants.variants:
         ret += mcgen('''
     case %(case)s:
-        visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, name, &err);
+        visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
         break;
 ''',
                      case=var.type.alternate_qtype(),
@@ -255,11 +255,11 @@ def gen_visit_union(name, base, variants):

     ret += mcgen('''

-void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;

-    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
+    visit_start_struct(v, name, (void **)obj, "%(name)s", sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
@@ -276,7 +276,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
                      c_name=base.c_name())
     else:
         ret += mcgen('''
-    visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
+    visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
 ''',
                      c_type=variants.tag_member.type.c_name(),
                      c_name=c_name(variants.tag_member.name),
@@ -300,7 +300,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
                                        var.name))
         if simple_union_type:
             ret += mcgen('''
-        visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, "data", &err);
+        visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
 ''',
                          c_type=simple_union_type.c_name(),
                          c_name=c_name(var.name))
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 497eaba..3b4c62e 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -1647,7 +1647,7 @@ def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False,
     for memb in members:
         if memb.optional:
             ret += mcgen('''
-    if (visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s")) {
+    if (visit_optional(v, "%(name)s", &%(prefix)shas_%(c_name)s)) {
 ''',
                          prefix=prefix, c_name=c_name(memb.name),
                          name=memb.name, errp=errparg)
@@ -1660,7 +1660,7 @@ def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False,
             cast = ''

         ret += mcgen('''
-    visit_type_%(c_type)s(v, %(cast)s&%(prefix)s%(c_name)s, "%(name)s", %(errp)s);
+    visit_type_%(c_type)s(v, "%(name)s", %(cast)s&%(prefix)s%(c_name)s, %(errp)s);
 ''',
                      c_type=memb.type.c_name(), prefix=prefix, cast=cast,
                      c_name=c_name(memb.name), name=memb.name,
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 13c5b69..110fa1c 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1520,7 +1520,7 @@ static void x86_cpuid_version_get_family(Object *obj, Visitor *v, void *opaque,
     if (value == 0xf) {
         value += (env->cpuid_version >> 20) & 0xff;
     }
-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque,
@@ -1533,7 +1533,7 @@ static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque,
     Error *local_err = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &local_err);
+    visit_type_int(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1561,7 +1561,7 @@ static void x86_cpuid_version_get_model(Object *obj, Visitor *v, void *opaque,

     value = (env->cpuid_version >> 4) & 0xf;
     value |= ((env->cpuid_version >> 16) & 0xf) << 4;
-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque,
@@ -1574,7 +1574,7 @@ static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque,
     Error *local_err = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &local_err);
+    visit_type_int(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1598,7 +1598,7 @@ static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v,
     int64_t value;

     value = env->cpuid_version & 0xf;
-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v,
@@ -1612,7 +1612,7 @@ static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v,
     Error *local_err = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &local_err);
+    visit_type_int(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1705,7 +1705,7 @@ static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, void *opaque,
     int64_t value;

     value = cpu->env.tsc_khz * 1000;
-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
@@ -1717,7 +1717,7 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
     Error *local_err = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &local_err);
+    visit_type_int(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -1737,7 +1737,7 @@ static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque,
     X86CPU *cpu = X86_CPU(obj);
     int64_t value = cpu->apic_id;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque,
@@ -1756,7 +1756,7 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque,
         return;
     }

-    visit_type_int(v, &value, name, &error);
+    visit_type_int(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
@@ -1801,7 +1801,7 @@ static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque,
         list = &list_entries[w];
     }

-    visit_type_X86CPUFeatureWordInfoList(v, &list, "feature-words", &err);
+    visit_type_X86CPUFeatureWordInfoList(v, "feature-words", &list, &err);
     error_propagate(errp, err);
 }

@@ -1811,7 +1811,7 @@ static void x86_get_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
     X86CPU *cpu = X86_CPU(obj);
     int64_t value = cpu->hyperv_spinlock_attempts;

-    visit_type_int(v, &value, name, errp);
+    visit_type_int(v, name, &value, errp);
 }

 static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
@@ -1823,7 +1823,7 @@ static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
     Error *err = NULL;
     int64_t value;

-    visit_type_int(v, &value, name, &err);
+    visit_type_int(v, name, &value, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -2928,7 +2928,7 @@ static void x86_cpu_get_bit_prop(Object *obj,
 {
     BitProperty *fp = opaque;
     bool value = (*fp->ptr & fp->mask) == fp->mask;
-    visit_type_bool(v, &value, name, errp);
+    visit_type_bool(v, name, &value, errp);
 }

 static void x86_cpu_set_bit_prop(Object *obj,
@@ -2947,7 +2947,7 @@ static void x86_cpu_set_bit_prop(Object *obj,
         return;
     }

-    visit_type_bool(v, &value, name, &local_err);
+    visit_type_bool(v, name, &value, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 4ab2d92..9d7936b 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8059,7 +8059,7 @@ static void powerpc_get_compat(Object *obj, Visitor *v,
         break;
     }

-    visit_type_str(v, &value, name, errp);
+    visit_type_str(v, name, &value, errp);
 }

 static void powerpc_set_compat(Object *obj, Visitor *v,
@@ -8070,7 +8070,7 @@ static void powerpc_set_compat(Object *obj, Visitor *v,
     Property *prop = opaque;
     uint32_t *max_compat = qdev_get_prop_ptr(DEVICE(obj), prop);

-    visit_type_str(v, &value, name, &error);
+    visit_type_str(v, name, &value, &error);
     if (error) {
         error_propagate(errp, error);
         return;
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index 1c753d9..9600b97 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -44,7 +44,7 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data)
     g_assert(opts != NULL);

     ov = opts_visitor_new(opts);
-    visit_type_UserDefOptions(opts_get_visitor(ov), &f->userdef, NULL,
+    visit_type_UserDefOptions(opts_get_visitor(ov), NULL, &f->userdef,
                               &f->err);
     opts_visitor_cleanup(ov);
     qemu_opts_del(opts);
@@ -58,8 +58,8 @@ teardown_fixture(OptsVisitorFixture *f, gconstpointer test_data)
         QapiDeallocVisitor *dv;

         dv = qapi_dealloc_visitor_new();
-        visit_type_UserDefOptions(qapi_dealloc_get_visitor(dv), &f->userdef,
-                                  NULL, NULL);
+        visit_type_UserDefOptions(qapi_dealloc_get_visitor(dv), NULL,
+                                  &f->userdef, NULL);
         qapi_dealloc_visitor_cleanup(dv);
     }
     error_free(f->err);
diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
index 0be9835..e728611 100644
--- a/tests/test-qdev-global-props.c
+++ b/tests/test-qdev-global-props.c
@@ -124,7 +124,7 @@ static void prop1_accessor(Object *obj,
 {
     MyType *mt = DYNAMIC_TYPE(obj);

-    visit_type_uint32(v, &mt->prop1, name, errp);
+    visit_type_uint32(v, name, &mt->prop1, errp);
 }

 static void prop2_accessor(Object *obj,
@@ -135,7 +135,7 @@ static void prop2_accessor(Object *obj,
 {
     MyType *mt = DYNAMIC_TYPE(obj);

-    visit_type_uint32(v, &mt->prop2, name, errp);
+    visit_type_uint32(v, name, &mt->prop2, errp);
 }

 static void dynamic_instance_init(Object *obj)
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 9f35b80..4d267b6 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -217,7 +217,7 @@ static void test_dealloc_partial(void)
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));

         qiv = qmp_input_visitor_new(QOBJECT(ud2_dict));
-        visit_type_UserDefTwo(qmp_input_get_visitor(qiv), &ud2, NULL, &err);
+        visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err);
         qmp_input_visitor_cleanup(qiv);
         QDECREF(ud2_dict);
     }
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index f1c2e3b..775ad39 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -98,7 +98,7 @@ static void test_validate_struct(TestInputVisitorData *data,

     v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");

-    visit_type_TestStruct(v, &p, NULL, &error_abort);
+    visit_type_TestStruct(v, NULL, &p, &error_abort);
     g_free(p->string);
     g_free(p);
 }
@@ -114,7 +114,7 @@ static void test_validate_struct_nested(TestInputVisitorData *data,
                            "'dict2': { 'userdef': { 'integer': 42, "
                            "'string': 'string' }, 'string': 'string2'}}}");

-    visit_type_UserDefTwo(v, &udp, NULL, &error_abort);
+    visit_type_UserDefTwo(v, NULL, &udp, &error_abort);
     qapi_free_UserDefTwo(udp);
 }

@@ -126,7 +126,7 @@ static void test_validate_list(TestInputVisitorData *data,

     v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");

-    visit_type_UserDefOneList(v, &head, NULL, &error_abort);
+    visit_type_UserDefOneList(v, NULL, &head, &error_abort);
     qapi_free_UserDefOneList(head);
 }

@@ -138,7 +138,7 @@ static void test_validate_union_native_list(TestInputVisitorData *data,

     v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 1, 2 ] }");

-    visit_type_UserDefNativeListUnion(v, &tmp, NULL, &error_abort);
+    visit_type_UserDefNativeListUnion(v, NULL, &tmp, &error_abort);
     qapi_free_UserDefNativeListUnion(tmp);
 }

@@ -154,7 +154,7 @@ static void test_validate_union_flat(TestInputVisitorData *data,
                            "'string': 'str', "
                            "'boolean': true }");

-    visit_type_UserDefFlatUnion(v, &tmp, NULL, &error_abort);
+    visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort);
     qapi_free_UserDefFlatUnion(tmp);
 }

@@ -166,7 +166,7 @@ static void test_validate_alternate(TestInputVisitorData *data,

     v = validate_test_init(data, "42");

-    visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
+    visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
     qapi_free_UserDefAlternate(tmp);
 }

@@ -179,7 +179,7 @@ static void test_validate_fail_struct(TestInputVisitorData *data,

     v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }");

-    visit_type_TestStruct(v, &p, NULL, &err);
+    visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
     if (p) {
         g_free(p->string);
@@ -196,7 +196,7 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data,

     v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");

-    visit_type_UserDefTwo(v, &udp, NULL, &err);
+    visit_type_UserDefTwo(v, NULL, &udp, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefTwo(udp);
 }
@@ -210,7 +210,7 @@ static void test_validate_fail_list(TestInputVisitorData *data,

     v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]");

-    visit_type_UserDefOneList(v, &head, NULL, &err);
+    visit_type_UserDefOneList(v, NULL, &head, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefOneList(head);
 }
@@ -225,7 +225,7 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data,
     v = validate_test_init(data,
                            "{ 'type': 'integer', 'data' : [ 'string' ] }");

-    visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
+    visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefNativeListUnion(tmp);
 }
@@ -239,7 +239,7 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data,

     v = validate_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }");

-    visit_type_UserDefFlatUnion(v, &tmp, NULL, &err);
+    visit_type_UserDefFlatUnion(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefFlatUnion(tmp);
 }
@@ -254,7 +254,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
     /* test situation where discriminator field ('enum1' here) is missing */
     v = validate_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }");

-    visit_type_UserDefFlatUnion2(v, &tmp, NULL, &err);
+    visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefFlatUnion2(tmp);
 }
@@ -268,7 +268,7 @@ static void test_validate_fail_alternate(TestInputVisitorData *data,

     v = validate_test_init(data, "3.14");

-    visit_type_UserDefAlternate(v, &tmp, NULL, &err);
+    visit_type_UserDefAlternate(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefAlternate(tmp);
 }
@@ -281,7 +281,7 @@ static void do_test_validate_qmp_introspect(TestInputVisitorData *data,

     v = validate_test_init_raw(data, schema_json);

-    visit_type_SchemaInfoList(v, &schema, NULL, &error_abort);
+    visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
     g_assert(schema);

     qapi_free_SchemaInfoList(schema);
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index b4a5bee..f6bd408 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -93,7 +93,7 @@ static void test_visitor_in_int(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "%" PRId64, value);

-    visit_type_int(v, &res, NULL, &error_abort);
+    visit_type_int(v, NULL, &res, &error_abort);
     g_assert_cmpint(res, ==, value);
 }

@@ -110,7 +110,7 @@ static void test_visitor_in_int_overflow(TestInputVisitorData *data,
      */
     v = visitor_input_test_init(data, "%f", DBL_MAX);

-    visit_type_int(v, &res, NULL, &err);
+    visit_type_int(v, NULL, &res, &err);
     error_free_or_abort(&err);
 }

@@ -122,7 +122,7 @@ static void test_visitor_in_bool(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "true");

-    visit_type_bool(v, &res, NULL, &error_abort);
+    visit_type_bool(v, NULL, &res, &error_abort);
     g_assert_cmpint(res, ==, true);
 }

@@ -134,7 +134,7 @@ static void test_visitor_in_number(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "%f", value);

-    visit_type_number(v, &res, NULL, &error_abort);
+    visit_type_number(v, NULL, &res, &error_abort);
     g_assert_cmpfloat(res, ==, value);
 }

@@ -146,7 +146,7 @@ static void test_visitor_in_string(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "%s", value);

-    visit_type_str(v, &res, NULL, &error_abort);
+    visit_type_str(v, NULL, &res, &error_abort);
     g_assert_cmpstr(res, ==, value);

     g_free(res);
@@ -163,7 +163,7 @@ static void test_visitor_in_enum(TestInputVisitorData *data,

         v = visitor_input_test_init(data, "%s", EnumOne_lookup[i]);

-        visit_type_EnumOne(v, &res, NULL, &error_abort);
+        visit_type_EnumOne(v, NULL, &res, &error_abort);
         g_assert_cmpint(i, ==, res);
     }
 }
@@ -177,7 +177,7 @@ static void test_visitor_in_struct(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");

-    visit_type_TestStruct(v, &p, NULL, &error_abort);
+    visit_type_TestStruct(v, NULL, &p, &error_abort);
     g_assert_cmpint(p->integer, ==, -42);
     g_assert(p->boolean == true);
     g_assert_cmpstr(p->string, ==, "foo");
@@ -197,7 +197,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data,
                                 "'dict2': { 'userdef': { 'integer': 42, "
                                 "'string': 'string' }, 'string': 'string2'}}}");

-    visit_type_UserDefTwo(v, &udp, NULL, &error_abort);
+    visit_type_UserDefTwo(v, NULL, &udp, &error_abort);

     g_assert_cmpstr(udp->string0, ==, "string0");
     g_assert_cmpstr(udp->dict1->string1, ==, "string1");
@@ -218,7 +218,7 @@ static void test_visitor_in_list(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");

-    visit_type_UserDefOneList(v, &head, NULL, &error_abort);
+    visit_type_UserDefOneList(v, NULL, &head, &error_abort);
     g_assert(head != NULL);

     for (i = 0, item = head; item; item = item->next, i++) {
@@ -234,7 +234,7 @@ static void test_visitor_in_list(TestInputVisitorData *data,

     /* An empty list is valid */
     v = visitor_input_test_init(data, "[]");
-    visit_type_UserDefOneList(v, &head, NULL, &error_abort);
+    visit_type_UserDefOneList(v, NULL, &head, &error_abort);
     g_assert(!head);
 }

@@ -250,14 +250,14 @@ static void test_visitor_in_any(TestInputVisitorData *data,
     QObject *qobj;

     v = visitor_input_test_init(data, "-42");
-    visit_type_any(v, &res, NULL, &error_abort);
+    visit_type_any(v, NULL, &res, &error_abort);
     qint = qobject_to_qint(res);
     g_assert(qint);
     g_assert_cmpint(qint_get_int(qint), ==, -42);
     qobject_decref(res);

     v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
-    visit_type_any(v, &res, NULL, &error_abort);
+    visit_type_any(v, NULL, &res, &error_abort);
     qdict = qobject_to_qdict(res);
     g_assert(qdict && qdict_size(qdict) == 3);
     qobj = qdict_get(qdict, "integer");
@@ -291,7 +291,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
                                 "'string': 'str', "
                                 "'boolean': true }");

-    visit_type_UserDefFlatUnion(v, &tmp, NULL, &error_abort);
+    visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort);
     g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1);
     g_assert_cmpstr(tmp->string, ==, "str");
     g_assert_cmpint(tmp->integer, ==, 41);
@@ -311,19 +311,19 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,
     UserDefAlternate *tmp;

     v = visitor_input_test_init(data, "42");
-    visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
+    visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
     g_assert_cmpint(tmp->type, ==, QTYPE_QINT);
     g_assert_cmpint(tmp->u.i, ==, 42);
     qapi_free_UserDefAlternate(tmp);

     v = visitor_input_test_init(data, "'string'");
-    visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
+    visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
     g_assert_cmpint(tmp->type, ==, QTYPE_QSTRING);
     g_assert_cmpstr(tmp->u.s, ==, "string");
     qapi_free_UserDefAlternate(tmp);

     v = visitor_input_test_init(data, "false");
-    visit_type_UserDefAlternate(v, &tmp, NULL, &err);
+    visit_type_UserDefAlternate(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
     qapi_free_UserDefAlternate(tmp);
 }
@@ -343,36 +343,36 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
     /* Parsing an int */

     v = visitor_input_test_init(data, "42");
-    visit_type_AltStrBool(v, &asb, NULL, &err);
+    visit_type_AltStrBool(v, NULL, &asb, &err);
     error_free_or_abort(&err);
     qapi_free_AltStrBool(asb);

     v = visitor_input_test_init(data, "42");
-    visit_type_AltStrNum(v, &asn, NULL, &error_abort);
+    visit_type_AltStrNum(v, NULL, &asn, &error_abort);
     g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(asn->u.n, ==, 42);
     qapi_free_AltStrNum(asn);

     v = visitor_input_test_init(data, "42");
-    visit_type_AltNumStr(v, &ans, NULL, &error_abort);
+    visit_type_AltNumStr(v, NULL, &ans, &error_abort);
     g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(ans->u.n, ==, 42);
     qapi_free_AltNumStr(ans);

     v = visitor_input_test_init(data, "42");
-    visit_type_AltStrInt(v, &asi, NULL, &error_abort);
+    visit_type_AltStrInt(v, NULL, &asi, &error_abort);
     g_assert_cmpint(asi->type, ==, QTYPE_QINT);
     g_assert_cmpint(asi->u.i, ==, 42);
     qapi_free_AltStrInt(asi);

     v = visitor_input_test_init(data, "42");
-    visit_type_AltIntNum(v, &ain, NULL, &error_abort);
+    visit_type_AltIntNum(v, NULL, &ain, &error_abort);
     g_assert_cmpint(ain->type, ==, QTYPE_QINT);
     g_assert_cmpint(ain->u.i, ==, 42);
     qapi_free_AltIntNum(ain);

     v = visitor_input_test_init(data, "42");
-    visit_type_AltNumInt(v, &ani, NULL, &error_abort);
+    visit_type_AltNumInt(v, NULL, &ani, &error_abort);
     g_assert_cmpint(ani->type, ==, QTYPE_QINT);
     g_assert_cmpint(ani->u.i, ==, 42);
     qapi_free_AltNumInt(ani);
@@ -380,35 +380,35 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
     /* Parsing a double */

     v = visitor_input_test_init(data, "42.5");
-    visit_type_AltStrBool(v, &asb, NULL, &err);
+    visit_type_AltStrBool(v, NULL, &asb, &err);
     error_free_or_abort(&err);
     qapi_free_AltStrBool(asb);

     v = visitor_input_test_init(data, "42.5");
-    visit_type_AltStrNum(v, &asn, NULL, &error_abort);
+    visit_type_AltStrNum(v, NULL, &asn, &error_abort);
     g_assert_cmpint(asn->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(asn->u.n, ==, 42.5);
     qapi_free_AltStrNum(asn);

     v = visitor_input_test_init(data, "42.5");
-    visit_type_AltNumStr(v, &ans, NULL, &error_abort);
+    visit_type_AltNumStr(v, NULL, &ans, &error_abort);
     g_assert_cmpint(ans->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(ans->u.n, ==, 42.5);
     qapi_free_AltNumStr(ans);

     v = visitor_input_test_init(data, "42.5");
-    visit_type_AltStrInt(v, &asi, NULL, &err);
+    visit_type_AltStrInt(v, NULL, &asi, &err);
     error_free_or_abort(&err);
     qapi_free_AltStrInt(asi);

     v = visitor_input_test_init(data, "42.5");
-    visit_type_AltIntNum(v, &ain, NULL, &error_abort);
+    visit_type_AltIntNum(v, NULL, &ain, &error_abort);
     g_assert_cmpint(ain->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(ain->u.n, ==, 42.5);
     qapi_free_AltIntNum(ain);

     v = visitor_input_test_init(data, "42.5");
-    visit_type_AltNumInt(v, &ani, NULL, &error_abort);
+    visit_type_AltNumInt(v, NULL, &ani, &error_abort);
     g_assert_cmpint(ani->type, ==, QTYPE_QFLOAT);
     g_assert_cmpfloat(ani->u.n, ==, 42.5);
     qapi_free_AltNumInt(ani);
@@ -435,7 +435,7 @@ static void test_native_list_integer_helper(TestInputVisitorData *data,
                            gstr_list->str);
     v = visitor_input_test_init_raw(data,  gstr_union->str);

-    visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
+    visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort);
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, kind);

@@ -596,7 +596,7 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data,
                            gstr_list->str);
     v = visitor_input_test_init_raw(data,  gstr_union->str);

-    visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
+    visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort);
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN);

@@ -629,7 +629,7 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data,
                            gstr_list->str);
     v = visitor_input_test_init_raw(data,  gstr_union->str);

-    visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
+    visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort);
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING);

@@ -666,7 +666,7 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data,
                            gstr_list->str);
     v = visitor_input_test_init_raw(data,  gstr_union->str);

-    visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
+    visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort);
     g_assert(cvalue != NULL);
     g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER);

@@ -706,7 +706,7 @@ static void test_visitor_in_errors(TestInputVisitorData *data,
     v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', "
                                 "'string': -42 }");

-    visit_type_TestStruct(v, &p, NULL, &err);
+    visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
     /* FIXME - a failed parse should not leave a partially-allocated p
      * for us to clean up; this could cause callers to leak memory. */
@@ -716,7 +716,7 @@ static void test_visitor_in_errors(TestInputVisitorData *data,
     g_free(p);

     v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]");
-    visit_type_strList(v, &q, NULL, &err);
+    visit_type_strList(v, NULL, &q, &err);
     error_free_or_abort(&err);
     assert(q);
     qapi_free_strList(q);
@@ -734,35 +734,35 @@ static void test_visitor_in_wrong_type(TestInputVisitorData *data,
     /* Make sure arrays and structs cannot be confused */

     v = visitor_input_test_init(data, "[]");
-    visit_type_TestStruct(v, &p, NULL, &err);
+    visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
     g_assert(!p);

     v = visitor_input_test_init(data, "{}");
-    visit_type_strList(v, &q, NULL, &err);
+    visit_type_strList(v, NULL, &q, &err);
     error_free_or_abort(&err);
     assert(!q);

     /* Make sure primitives and struct cannot be confused */

     v = visitor_input_test_init(data, "1");
-    visit_type_TestStruct(v, &p, NULL, &err);
+    visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
     g_assert(!p);

     v = visitor_input_test_init(data, "{}");
-    visit_type_int(v, &i, NULL, &err);
+    visit_type_int(v, NULL, &i, &err);
     error_free_or_abort(&err);

     /* Make sure primitives and arrays cannot be confused */

     v = visitor_input_test_init(data, "1");
-    visit_type_strList(v, &q, NULL, &err);
+    visit_type_strList(v, NULL, &q, &err);
     error_free_or_abort(&err);
     assert(!q);

     v = visitor_input_test_init(data, "[]");
-    visit_type_int(v, &i, NULL, &err);
+    visit_type_int(v, NULL, &i, &err);
     error_free_or_abort(&err);
 }

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 3078442..4df94bc 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -47,7 +47,7 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
     int64_t value = -42;
     QObject *obj;

-    visit_type_int(data->ov, &value, NULL, &error_abort);
+    visit_type_int(data->ov, NULL, &value, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -63,7 +63,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
     bool value = true;
     QObject *obj;

-    visit_type_bool(data->ov, &value, NULL, &error_abort);
+    visit_type_bool(data->ov, NULL, &value, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -79,7 +79,7 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
     double value = 3.14;
     QObject *obj;

-    visit_type_number(data->ov, &value, NULL, &error_abort);
+    visit_type_number(data->ov, NULL, &value, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -95,7 +95,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
     char *string = (char *) "Q E M U";
     QObject *obj;

-    visit_type_str(data->ov, &string, NULL, &error_abort);
+    visit_type_str(data->ov, NULL, &string, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -112,7 +112,7 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
     QObject *obj;

     /* A null string should return "" */
-    visit_type_str(data->ov, &string, NULL, &error_abort);
+    visit_type_str(data->ov, NULL, &string, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -129,7 +129,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
     EnumOne i;

     for (i = 0; i < ENUM_ONE__MAX; i++) {
-        visit_type_EnumOne(data->ov, &i, "unused", &error_abort);
+        visit_type_EnumOne(data->ov, "unused", &i, &error_abort);

         obj = qmp_output_get_qobject(data->qov);
         g_assert(obj != NULL);
@@ -148,7 +148,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,

     for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
         err = NULL;
-        visit_type_EnumOne(data->ov, &bad_values[i], "unused", &err);
+        visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
         g_assert(err);
         error_free(err);
     }
@@ -165,7 +165,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
     QObject *obj;
     QDict *qdict;

-    visit_type_TestStruct(data->ov, &p, NULL, &error_abort);
+    visit_type_TestStruct(data->ov, NULL, &p, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -210,7 +210,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
     ud2->dict1->dict3->userdef->integer = value;
     ud2->dict1->dict3->string = g_strdup(strings[3]);

-    visit_type_UserDefTwo(data->ov, &ud2, "unused", &error_abort);
+    visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -257,7 +257,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
         err = NULL;
         u.has_enum1 = true;
         u.enum1 = bad_values[i];
-        visit_type_UserDefOne(data->ov, &pu, "unused", &err);
+        visit_type_UserDefOne(data->ov, "unused", &pu, &err);
         g_assert(err);
         error_free(err);
     }
@@ -289,7 +289,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
         head = p;
     }

-    visit_type_TestStructList(data->ov, &head, NULL, &error_abort);
+    visit_type_TestStructList(data->ov, NULL, &head, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -356,7 +356,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     QObject *obj;

     qobj = QOBJECT(qint_from_int(-42));
-    visit_type_any(data->ov, &qobj, NULL, &error_abort);
+    visit_type_any(data->ov, NULL, &qobj, &error_abort);
     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
     g_assert(qobject_type(obj) == QTYPE_QINT);
@@ -369,7 +369,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qdict_put(qdict, "boolean", qbool_from_bool(true));
     qdict_put(qdict, "string", qstring_from_str("foo"));
     qobj = QOBJECT(qdict);
-    visit_type_any(data->ov, &qobj, NULL, &error_abort);
+    visit_type_any(data->ov, NULL, &qobj, &error_abort);
     qobject_decref(qobj);
     obj = qmp_output_get_qobject(data->qov);
     g_assert(obj != NULL);
@@ -406,7 +406,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     tmp->integer = 41;
     tmp->u.value1->boolean = true;

-    visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &error_abort);
+    visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
     arg = qmp_output_get_qobject(data->qov);

     g_assert(qobject_type(arg) == QTYPE_QDICT);
@@ -431,7 +431,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->type = QTYPE_QINT;
     tmp->u.i = 42;

-    visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
+    visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
     arg = qmp_output_get_qobject(data->qov);

     g_assert(qobject_type(arg) == QTYPE_QINT);
@@ -444,7 +444,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->type = QTYPE_QSTRING;
     tmp->u.s = g_strdup("hello");

-    visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
+    visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
     arg = qmp_output_get_qobject(data->qov);

     g_assert(qobject_type(arg) == QTYPE_QSTRING);
@@ -690,7 +690,7 @@ static void test_native_list(TestOutputVisitorData *data,
     cvalue->type = kind;
     init_native_list(cvalue);

-    visit_type_UserDefNativeListUnion(data->ov, &cvalue, NULL, &error_abort);
+    visit_type_UserDefNativeListUnion(data->ov, NULL, &cvalue, &error_abort);

     obj = qmp_output_get_qobject(data->qov);
     check_native_list(obj, cvalue->type);
diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 8e3433e..d99498d 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -59,7 +59,7 @@ static void test_visitor_in_int(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "-42");

-    visit_type_int(v, &res, NULL, &err);
+    visit_type_int(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, value);
 }
@@ -74,7 +74,7 @@ static void test_visitor_in_intList(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8");

-    visit_type_int16List(v, &res, NULL, &error_abort);
+    visit_type_int16List(v, NULL, &res, &error_abort);
     tmp = res;
     while (i < sizeof(value) / sizeof(value[0])) {
         g_assert(tmp);
@@ -100,42 +100,42 @@ static void test_visitor_in_bool(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "true");

-    visit_type_bool(v, &res, NULL, &err);
+    visit_type_bool(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, true);
     visitor_input_teardown(data, unused);

     v = visitor_input_test_init(data, "yes");

-    visit_type_bool(v, &res, NULL, &err);
+    visit_type_bool(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, true);
     visitor_input_teardown(data, unused);

     v = visitor_input_test_init(data, "on");

-    visit_type_bool(v, &res, NULL, &err);
+    visit_type_bool(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, true);
     visitor_input_teardown(data, unused);

     v = visitor_input_test_init(data, "false");

-    visit_type_bool(v, &res, NULL, &err);
+    visit_type_bool(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, false);
     visitor_input_teardown(data, unused);

     v = visitor_input_test_init(data, "no");

-    visit_type_bool(v, &res, NULL, &err);
+    visit_type_bool(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, false);
     visitor_input_teardown(data, unused);

     v = visitor_input_test_init(data, "off");

-    visit_type_bool(v, &res, NULL, &err);
+    visit_type_bool(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, false);
 }
@@ -149,7 +149,7 @@ static void test_visitor_in_number(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "3.14");

-    visit_type_number(v, &res, NULL, &err);
+    visit_type_number(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpfloat(res, ==, value);
 }
@@ -163,7 +163,7 @@ static void test_visitor_in_string(TestInputVisitorData *data,

     v = visitor_input_test_init(data, value);

-    visit_type_str(v, &res, NULL, &err);
+    visit_type_str(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpstr(res, ==, value);

@@ -182,7 +182,7 @@ static void test_visitor_in_enum(TestInputVisitorData *data,

         v = visitor_input_test_init(data, EnumOne_lookup[i]);

-        visit_type_EnumOne(v, &res, NULL, &err);
+        visit_type_EnumOne(v, NULL, &res, &err);
         g_assert(!err);
         g_assert_cmpint(i, ==, res);

@@ -220,29 +220,29 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
         }

         v = visitor_input_test_init(data, buf);
-        visit_type_int(v, &ires, NULL, NULL);
+        visit_type_int(v, NULL, &ires, NULL);
         visitor_input_teardown(data, NULL);

         v = visitor_input_test_init(data, buf);
-        visit_type_intList(v, &ilres, NULL, NULL);
+        visit_type_intList(v, NULL, &ilres, NULL);
         visitor_input_teardown(data, NULL);

         v = visitor_input_test_init(data, buf);
-        visit_type_bool(v, &bres, NULL, NULL);
+        visit_type_bool(v, NULL, &bres, NULL);
         visitor_input_teardown(data, NULL);

         v = visitor_input_test_init(data, buf);
-        visit_type_number(v, &nres, NULL, NULL);
+        visit_type_number(v, NULL, &nres, NULL);
         visitor_input_teardown(data, NULL);

         v = visitor_input_test_init(data, buf);
         sres = NULL;
-        visit_type_str(v, &sres, NULL, NULL);
+        visit_type_str(v, NULL, &sres, NULL);
         g_free(sres);
         visitor_input_teardown(data, NULL);

         v = visitor_input_test_init(data, buf);
-        visit_type_EnumOne(v, &eres, NULL, NULL);
+        visit_type_EnumOne(v, NULL, &eres, NULL);
         visitor_input_teardown(data, NULL);
     }
 }
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index 7aecdfc..4f4450f 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -61,7 +61,7 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
     Error *err = NULL;
     char *str;

-    visit_type_int(data->ov, &value, NULL, &err);
+    visit_type_int(data->ov, NULL, &value, &err);
     g_assert(!err);

     str = string_output_get_string(data->sov);
@@ -90,7 +90,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
         tmp = &(*tmp)->next;
     }

-    visit_type_intList(data->ov, &list, NULL, &err);
+    visit_type_intList(data->ov, NULL, &list, &err);
     g_assert(err == NULL);

     str = string_output_get_string(data->sov);
@@ -120,7 +120,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
     bool value = true;
     char *str;

-    visit_type_bool(data->ov, &value, NULL, &err);
+    visit_type_bool(data->ov, NULL, &value, &err);
     g_assert(!err);

     str = string_output_get_string(data->sov);
@@ -136,7 +136,7 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
     Error *err = NULL;
     char *str;

-    visit_type_number(data->ov, &value, NULL, &err);
+    visit_type_number(data->ov, NULL, &value, &err);
     g_assert(!err);

     str = string_output_get_string(data->sov);
@@ -153,7 +153,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
     Error *err = NULL;
     char *str;

-    visit_type_str(data->ov, &string, NULL, &err);
+    visit_type_str(data->ov, NULL, &string, &err);
     g_assert(!err);

     str = string_output_get_string(data->sov);
@@ -174,7 +174,7 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
     char *str;

     /* A null string should return "" */
-    visit_type_str(data->ov, &string, NULL, &err);
+    visit_type_str(data->ov, NULL, &string, &err);
     g_assert(!err);

     str = string_output_get_string(data->sov);
@@ -197,7 +197,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
     for (i = 0; i < ENUM_ONE__MAX; i++) {
         char *str_human;

-        visit_type_EnumOne(data->ov, &i, "unused", &err);
+        visit_type_EnumOne(data->ov, "unused", &i, &err);
         g_assert(!err);

         str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
@@ -222,7 +222,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,

     for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
         err = NULL;
-        visit_type_EnumOne(data->ov, &bad_values[i], "unused", &err);
+        visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
         g_assert(err);
         error_free(err);
     }
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 9f67f9e..f74a6df 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -101,40 +101,40 @@ static void visit_primitive_type(Visitor *v, void **native, Error **errp)
     PrimitiveType *pt = *native;
     switch(pt->type) {
     case PTYPE_STRING:
-        visit_type_str(v, (char **)&pt->value.string, NULL, errp);
+        visit_type_str(v, NULL, (char **)&pt->value.string, errp);
         break;
     case PTYPE_BOOLEAN:
-        visit_type_bool(v, &pt->value.boolean, NULL, errp);
+        visit_type_bool(v, NULL, &pt->value.boolean, errp);
         break;
     case PTYPE_NUMBER:
-        visit_type_number(v, &pt->value.number, NULL, errp);
+        visit_type_number(v, NULL, &pt->value.number, errp);
         break;
     case PTYPE_INTEGER:
-        visit_type_int(v, &pt->value.integer, NULL, errp);
+        visit_type_int(v, NULL, &pt->value.integer, errp);
         break;
     case PTYPE_U8:
-        visit_type_uint8(v, &pt->value.u8, NULL, errp);
+        visit_type_uint8(v, NULL, &pt->value.u8, errp);
         break;
     case PTYPE_U16:
-        visit_type_uint16(v, &pt->value.u16, NULL, errp);
+        visit_type_uint16(v, NULL, &pt->value.u16, errp);
         break;
     case PTYPE_U32:
-        visit_type_uint32(v, &pt->value.u32, NULL, errp);
+        visit_type_uint32(v, NULL, &pt->value.u32, errp);
         break;
     case PTYPE_U64:
-        visit_type_uint64(v, &pt->value.u64, NULL, errp);
+        visit_type_uint64(v, NULL, &pt->value.u64, errp);
         break;
     case PTYPE_S8:
-        visit_type_int8(v, &pt->value.s8, NULL, errp);
+        visit_type_int8(v, NULL, &pt->value.s8, errp);
         break;
     case PTYPE_S16:
-        visit_type_int16(v, &pt->value.s16, NULL, errp);
+        visit_type_int16(v, NULL, &pt->value.s16, errp);
         break;
     case PTYPE_S32:
-        visit_type_int32(v, &pt->value.s32, NULL, errp);
+        visit_type_int32(v, NULL, &pt->value.s32, errp);
         break;
     case PTYPE_S64:
-        visit_type_int64(v, &pt->value.s64, NULL, errp);
+        visit_type_int64(v, NULL, &pt->value.s64, errp);
         break;
     case PTYPE_EOL:
         g_assert_not_reached();
@@ -146,40 +146,40 @@ static void visit_primitive_list(Visitor *v, void **native, Error **errp)
     PrimitiveList *pl = *native;
     switch (pl->type) {
     case PTYPE_STRING:
-        visit_type_strList(v, &pl->value.strings, NULL, errp);
+        visit_type_strList(v, NULL, &pl->value.strings, errp);
         break;
     case PTYPE_BOOLEAN:
-        visit_type_boolList(v, &pl->value.booleans, NULL, errp);
+        visit_type_boolList(v, NULL, &pl->value.booleans, errp);
         break;
     case PTYPE_NUMBER:
-        visit_type_numberList(v, &pl->value.numbers, NULL, errp);
+        visit_type_numberList(v, NULL, &pl->value.numbers, errp);
         break;
     case PTYPE_INTEGER:
-        visit_type_intList(v, &pl->value.integers, NULL, errp);
+        visit_type_intList(v, NULL, &pl->value.integers, errp);
         break;
     case PTYPE_S8:
-        visit_type_int8List(v, &pl->value.s8_integers, NULL, errp);
+        visit_type_int8List(v, NULL, &pl->value.s8_integers, errp);
         break;
     case PTYPE_S16:
-        visit_type_int16List(v, &pl->value.s16_integers, NULL, errp);
+        visit_type_int16List(v, NULL, &pl->value.s16_integers, errp);
         break;
     case PTYPE_S32:
-        visit_type_int32List(v, &pl->value.s32_integers, NULL, errp);
+        visit_type_int32List(v, NULL, &pl->value.s32_integers, errp);
         break;
     case PTYPE_S64:
-        visit_type_int64List(v, &pl->value.s64_integers, NULL, errp);
+        visit_type_int64List(v, NULL, &pl->value.s64_integers, errp);
         break;
     case PTYPE_U8:
-        visit_type_uint8List(v, &pl->value.u8_integers, NULL, errp);
+        visit_type_uint8List(v, NULL, &pl->value.u8_integers, errp);
         break;
     case PTYPE_U16:
-        visit_type_uint16List(v, &pl->value.u16_integers, NULL, errp);
+        visit_type_uint16List(v, NULL, &pl->value.u16_integers, errp);
         break;
     case PTYPE_U32:
-        visit_type_uint32List(v, &pl->value.u32_integers, NULL, errp);
+        visit_type_uint32List(v, NULL, &pl->value.u32_integers, errp);
         break;
     case PTYPE_U64:
-        visit_type_uint64List(v, &pl->value.u64_integers, NULL, errp);
+        visit_type_uint64List(v, NULL, &pl->value.u64_integers, errp);
         break;
     default:
         g_assert_not_reached();
@@ -213,7 +213,7 @@ static void struct_cleanup(TestStruct *ts)

 static void visit_struct(Visitor *v, void **native, Error **errp)
 {
-    visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
+    visit_type_TestStruct(v, NULL, (TestStruct **)native, errp);
 }

 static UserDefTwo *nested_struct_create(void)
@@ -264,12 +264,12 @@ static void nested_struct_cleanup(UserDefTwo *udnp)

 static void visit_nested_struct(Visitor *v, void **native, Error **errp)
 {
-    visit_type_UserDefTwo(v, (UserDefTwo **)native, NULL, errp);
+    visit_type_UserDefTwo(v, NULL, (UserDefTwo **)native, errp);
 }

 static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
 {
-    visit_type_UserDefTwoList(v, (UserDefTwoList **)native, NULL, errp);
+    visit_type_UserDefTwoList(v, NULL, (UserDefTwoList **)native, errp);
 }

 /* test cases */
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 922efb3..96b7ae7 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1152,7 +1152,7 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,

     qov = qmp_output_visitor_new();
     ov = qmp_output_get_visitor(qov);
-    visit_type_SocketAddress(ov, &src, NULL, &error_abort);
+    visit_type_SocketAddress(ov, NULL, &src, &error_abort);
     obj = qmp_output_get_qobject(qov);
     qmp_output_visitor_cleanup(qov);
     if (!obj) {
@@ -1161,7 +1161,7 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,

     qiv = qmp_input_visitor_new(obj);
     iv = qmp_input_get_visitor(qiv);
-    visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
+    visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
     qmp_input_visitor_cleanup(qiv);
     qobject_decref(obj);
 }
diff --git a/vl.c b/vl.c
index aaa5403..49ec0de 100644
--- a/vl.c
+++ b/vl.c
@@ -2840,7 +2840,7 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     }

     qdict_del(pdict, "qom-type");
-    visit_type_str(v, &type, "qom-type", &err);
+    visit_type_str(v, "qom-type", &type, &err);
     if (err) {
         goto out;
     }
@@ -2850,7 +2850,7 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     }

     qdict_del(pdict, "id");
-    visit_type_str(v, &id, "id", &err);
+    visit_type_str(v, "id", &id, &err);
     if (err) {
         goto out_end;
     }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (13 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 18:49   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API Eric Blake
                   ` (22 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, open list:nvme, Eduardo Habkost, Paolo Bonzini,
	Michael S. Tsirkin, Jiri Slaby, Jason Wang, armbru,
	Alexander Graf, Keith Busch, Gonglei, open list:sPAPR,
	Gerd Hoffmann, Igor Mammedov, marcandre.lureau, David Gibson,
	John Snow, Andreas Färber, Richard Henderson

Similar to the previous patch, it's nice to have all functions
in the tree that involve a visitor and a name for conversion to
or from QAPI to consistently stick the 'name' parameter next
to the Visitor parameter.

Done by manually changing include/qom/object.h and qom/object.c,
then running this Coccinelle script and touching up the fallout
(Coccinelle insisted on adding some trailing whitespace).

    @ rule1 @
    identifier fn;
    type Object, Visitor, Error;
    identifier obj, v, opaque, name, errp;
    @@
     void fn
    - (Object *obj, Visitor *v, void *opaque, const char *name,
    + (Object *obj, Visitor *v, const char *name, void *opaque,
       Error **errp) { ... }

    @@
    identifier rule1.fn;
    expression obj, v, opaque, name, errp;
    @@
     fn(obj, v,
    -   opaque, name,
    +   name, opaque,
        errp)

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: typo fix in commit message, rebase to master context
v8: new patch
---
 backends/hostmem.c               |  16 +++---
 bootdevice.c                     |   8 +--
 hw/acpi/ich9.c                   |  35 +++++-------
 hw/block/nvme.c                  |   8 +--
 hw/core/machine.c                |  14 ++---
 hw/core/qdev-properties-system.c |  32 +++++------
 hw/core/qdev-properties.c        | 116 +++++++++++++++++++--------------------
 hw/core/qdev.c                   |   5 +-
 hw/i386/pc.c                     |  29 +++++-----
 hw/ide/qdev.c                    |   8 +--
 hw/intc/xics.c                   |  12 ++--
 hw/isa/lpc_ich9.c                |   5 +-
 hw/mem/pc-dimm.c                 |   4 +-
 hw/misc/edu.c                    |   4 +-
 hw/misc/tmp105.c                 |   8 +--
 hw/net/ne2000-isa.c              |  10 ++--
 hw/pci-host/piix.c               |  10 ++--
 hw/pci-host/q35.c                |  13 ++---
 hw/ppc/spapr_drc.c               |  16 +++---
 hw/usb/dev-storage.c             |   8 +--
 hw/virtio/virtio-balloon.c       |   8 +--
 include/qom/object.h             |   4 +-
 memory.c                         |  18 +++---
 net/dump.c                       |   8 +--
 net/filter-buffer.c              |  10 ++--
 qom/object.c                     |  75 +++++++++++++------------
 target-i386/cpu.c                |  66 +++++++++++-----------
 target-ppc/translate_init.c      |   8 +--
 tests/test-qdev-global-props.c   |  14 ++---
 29 files changed, 282 insertions(+), 290 deletions(-)

diff --git a/backends/hostmem.c b/backends/hostmem.c
index a9d30d8..2657907 100644
--- a/backends/hostmem.c
+++ b/backends/hostmem.c
@@ -26,8 +26,8 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
 #endif

 static void
-host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque,
-                             const char *name, Error **errp)
+host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
 {
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
     uint64_t value = backend->size;
@@ -36,8 +36,8 @@ host_memory_backend_get_size(Object *obj, Visitor *v, void *opaque,
 }

 static void
-host_memory_backend_set_size(Object *obj, Visitor *v, void *opaque,
-                             const char *name, Error **errp)
+host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
 {
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
     Error *local_err = NULL;
@@ -63,8 +63,8 @@ out:
 }

 static void
-host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
     uint16List *host_nodes = NULL;
@@ -95,8 +95,8 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, void *opaque,
 }

 static void
-host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
 #ifdef CONFIG_NUMA
     HostMemoryBackend *backend = MEMORY_BACKEND(obj);
diff --git a/bootdevice.c b/bootdevice.c
index d307cfc..56e5baf 100644
--- a/bootdevice.c
+++ b/bootdevice.c
@@ -270,15 +270,15 @@ typedef struct {
     DeviceState *dev;
 } BootIndexProperty;

-static void device_get_bootindex(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
+static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     BootIndexProperty *prop = opaque;
     visit_type_int32(v, name, prop->bootindex, errp);
 }

-static void device_set_bootindex(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
+static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     BootIndexProperty *prop = opaque;
     int32_t boot_index;
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 0a486fb..3efdce7 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -282,9 +282,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
     }
 }

-static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v,
-                                 void *opaque, const char *name,
-                                 Error **errp)
+static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     uint32_t value = pm->pm_io_base + ICH9_PMIO_GPE0_STS;
@@ -307,9 +306,8 @@ static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value,
     s->pm.acpi_memory_hotplug.is_enabled = value;
 }

-static void ich9_pm_get_disable_s3(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     uint8_t value = pm->disable_s3;
@@ -317,9 +315,8 @@ static void ich9_pm_get_disable_s3(Object *obj, Visitor *v,
     visit_type_uint8(v, name, &value, errp);
 }

-static void ich9_pm_set_disable_s3(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     Error *local_err = NULL;
@@ -334,9 +331,8 @@ out:
     error_propagate(errp, local_err);
 }

-static void ich9_pm_get_disable_s4(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void ich9_pm_get_disable_s4(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     uint8_t value = pm->disable_s4;
@@ -344,9 +340,8 @@ static void ich9_pm_get_disable_s4(Object *obj, Visitor *v,
     visit_type_uint8(v, name, &value, errp);
 }

-static void ich9_pm_set_disable_s4(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     Error *local_err = NULL;
@@ -361,9 +356,8 @@ out:
     error_propagate(errp, local_err);
 }

-static void ich9_pm_get_s4_val(Object *obj, Visitor *v,
-                               void *opaque, const char *name,
-                               Error **errp)
+static void ich9_pm_get_s4_val(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     uint8_t value = pm->s4_val;
@@ -371,9 +365,8 @@ static void ich9_pm_get_s4_val(Object *obj, Visitor *v,
     visit_type_uint8(v, name, &value, errp);
 }

-static void ich9_pm_set_s4_val(Object *obj, Visitor *v,
-                               void *opaque, const char *name,
-                               Error **errp)
+static void ich9_pm_set_s4_val(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     ICH9LPCPMRegs *pm = opaque;
     Error *local_err = NULL;
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 3676e2e..8d32ede 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -915,16 +915,16 @@ static void nvme_class_init(ObjectClass *oc, void *data)
     dc->vmsd = &nvme_vmstate;
 }

-static void nvme_get_bootindex(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void nvme_get_bootindex(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     NvmeCtrl *s = NVME(obj);

     visit_type_int32(v, name, &s->conf.bootindex, errp);
 }

-static void nvme_set_bootindex(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void nvme_set_bootindex(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     NvmeCtrl *s = NVME(obj);
     int32_t boot_index;
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 0fd1e73..3ec3efa 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -33,7 +33,7 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp)
 }

 static void machine_set_kernel_irqchip(Object *obj, Visitor *v,
-                                       void *opaque, const char *name,
+                                       const char *name, void *opaque,
                                        Error **errp)
 {
     Error *err = NULL;
@@ -68,7 +68,7 @@ static void machine_set_kernel_irqchip(Object *obj, Visitor *v,
 }

 static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v,
-                                       void *opaque, const char *name,
+                                       const char *name, void *opaque,
                                        Error **errp)
 {
     MachineState *ms = MACHINE(obj);
@@ -78,7 +78,7 @@ static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v,
 }

 static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v,
-                                       void *opaque, const char *name,
+                                       const char *name, void *opaque,
                                        Error **errp)
 {
     MachineState *ms = MACHINE(obj);
@@ -170,8 +170,8 @@ static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp)
 }

 static void machine_get_phandle_start(Object *obj, Visitor *v,
-                                       void *opaque, const char *name,
-                                       Error **errp)
+                                      const char *name, void *opaque,
+                                      Error **errp)
 {
     MachineState *ms = MACHINE(obj);
     int64_t value = ms->phandle_start;
@@ -180,8 +180,8 @@ static void machine_get_phandle_start(Object *obj, Visitor *v,
 }

 static void machine_set_phandle_start(Object *obj, Visitor *v,
-                                       void *opaque, const char *name,
-                                       Error **errp)
+                                      const char *name, void *opaque,
+                                      Error **errp)
 {
     MachineState *ms = MACHINE(obj);
     Error *error = NULL;
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 86e0b02..0f0e472 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -111,14 +111,14 @@ static char *print_drive(void *ptr)
     return g_strdup(blk_name(ptr));
 }

-static void get_drive(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
+static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque,
+                      Error **errp)
 {
     get_pointer(obj, v, opaque, print_drive, name, errp);
 }

-static void set_drive(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
+static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
+                      Error **errp)
 {
     set_pointer(obj, v, opaque, parse_drive, name, errp);
 }
@@ -172,14 +172,14 @@ static char *print_chr(void *ptr)
     return g_strdup(val);
 }

-static void get_chr(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
+static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
 {
     get_pointer(obj, v, opaque, print_chr, name, errp);
 }

-static void set_chr(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
+static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
 {
     set_pointer(obj, v, opaque, parse_chr, name, errp);
 }
@@ -193,8 +193,8 @@ PropertyInfo qdev_prop_chr = {
 };

 /* --- netdev device --- */
-static void get_netdev(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void get_netdev(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -205,8 +205,8 @@ static void get_netdev(Object *obj, Visitor *v, void *opaque,
     g_free(p);
 }

-static void set_netdev(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void set_netdev(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -292,8 +292,8 @@ static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
     return snprintf(dest, len, "<null>");
 }

-static void get_vlan(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -310,8 +310,8 @@ static void get_vlan(Object *obj, Visitor *v, void *opaque,
     visit_type_int32(v, name, &id, errp);
 }

-static void set_vlan(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 971219d..de927c6 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -41,8 +41,8 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
     return ptr;
 }

-static void get_enum(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -52,8 +52,8 @@ static void get_enum(Object *obj, Visitor *v, void *opaque,
                     prop->info->name, errp);
 }

-static void set_enum(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -87,8 +87,8 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val)
     }
 }

-static void prop_get_bit(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
+static void prop_get_bit(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -98,8 +98,8 @@ static void prop_get_bit(Object *obj, Visitor *v, void *opaque,
     visit_type_bool(v, name, &value, errp);
 }

-static void prop_set_bit(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
+static void prop_set_bit(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -145,8 +145,8 @@ static void bit64_prop_set(DeviceState *dev, Property *props, bool val)
     }
 }

-static void prop_get_bit64(Object *obj, Visitor *v, void *opaque,
-                           const char *name, Error **errp)
+static void prop_get_bit64(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -156,8 +156,8 @@ static void prop_get_bit64(Object *obj, Visitor *v, void *opaque,
     visit_type_bool(v, name, &value, errp);
 }

-static void prop_set_bit64(Object *obj, Visitor *v, void *opaque,
-                           const char *name, Error **errp)
+static void prop_set_bit64(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -186,8 +186,8 @@ PropertyInfo qdev_prop_bit64 = {

 /* --- bool --- */

-static void get_bool(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -196,8 +196,8 @@ static void get_bool(Object *obj, Visitor *v, void *opaque,
     visit_type_bool(v, name, ptr, errp);
 }

-static void set_bool(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -219,8 +219,8 @@ PropertyInfo qdev_prop_bool = {

 /* --- 8bit integer --- */

-static void get_uint8(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
+static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque,
+                      Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -229,8 +229,8 @@ static void get_uint8(Object *obj, Visitor *v, void *opaque,
     visit_type_uint8(v, name, ptr, errp);
 }

-static void set_uint8(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
+static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque,
+                      Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -252,8 +252,8 @@ PropertyInfo qdev_prop_uint8 = {

 /* --- 16bit integer --- */

-static void get_uint16(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void get_uint16(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -262,8 +262,8 @@ static void get_uint16(Object *obj, Visitor *v, void *opaque,
     visit_type_uint16(v, name, ptr, errp);
 }

-static void set_uint16(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void set_uint16(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -285,8 +285,8 @@ PropertyInfo qdev_prop_uint16 = {

 /* --- 32bit integer --- */

-static void get_uint32(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void get_uint32(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -295,8 +295,8 @@ static void get_uint32(Object *obj, Visitor *v, void *opaque,
     visit_type_uint32(v, name, ptr, errp);
 }

-static void set_uint32(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void set_uint32(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -310,8 +310,8 @@ static void set_uint32(Object *obj, Visitor *v, void *opaque,
     visit_type_uint32(v, name, ptr, errp);
 }

-static void get_int32(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
+static void get_int32(Object *obj, Visitor *v, const char *name, void *opaque,
+                      Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -320,8 +320,8 @@ static void get_int32(Object *obj, Visitor *v, void *opaque,
     visit_type_int32(v, name, ptr, errp);
 }

-static void set_int32(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
+static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque,
+                      Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -349,8 +349,8 @@ PropertyInfo qdev_prop_int32 = {

 /* --- 64bit integer --- */

-static void get_uint64(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void get_uint64(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -359,8 +359,8 @@ static void get_uint64(Object *obj, Visitor *v, void *opaque,
     visit_type_uint64(v, name, ptr, errp);
 }

-static void set_uint64(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void set_uint64(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -388,8 +388,8 @@ static void release_string(Object *obj, const char *name, void *opaque)
     g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop));
 }

-static void get_string(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void get_string(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -403,8 +403,8 @@ static void get_string(Object *obj, Visitor *v, void *opaque,
     }
 }

-static void set_string(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
+static void set_string(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -447,8 +447,8 @@ PropertyInfo qdev_prop_ptr = {
  *   01:02:03:04:05:06
  *   01-02-03-04-05-06
  */
-static void get_mac(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
+static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -463,8 +463,8 @@ static void get_mac(Object *obj, Visitor *v, void *opaque,
     visit_type_str(v, name, &p, errp);
 }

-static void set_mac(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
+static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -546,8 +546,8 @@ PropertyInfo qdev_prop_bios_chs_trans = {
 /*
  * bus-local address, i.e. "$slot" or "$slot.$fn"
  */
-static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
-                          const char *name, Error **errp)
+static void set_pci_devfn(Object *obj, Visitor *v, const char *name,
+                          void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -617,8 +617,8 @@ PropertyInfo qdev_prop_pci_devfn = {

 /* --- blocksize --- */

-static void set_blocksize(Object *obj, Visitor *v, void *opaque,
-                          const char *name, Error **errp)
+static void set_blocksize(Object *obj, Visitor *v, const char *name,
+                          void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -664,8 +664,8 @@ PropertyInfo qdev_prop_blocksize = {

 /* --- pci host address --- */

-static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
+static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -685,8 +685,8 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
  * Parse [<domain>:]<bus>:<slot>.<func>
  *   if <domain> is not supplied, it's assumed to be 0.
  */
-static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
+static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -799,8 +799,8 @@ static void array_element_release(Object *obj, const char *name, void *opaque)
     g_free(p);
 }

-static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
+static void set_prop_arraylen(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
 {
     /* Setter for the property which defines the length of a
      * variable-sized property array. As well as actually setting the
@@ -1082,8 +1082,8 @@ void qdev_prop_set_globals(DeviceState *dev)

 /* --- 64bit unsigned int 'size' type --- */

-static void get_size(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void get_size(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
@@ -1092,8 +1092,8 @@ static void get_size(Object *obj, Visitor *v, void *opaque,
     visit_type_size(v, name, ptr, errp);
 }

-static void set_size(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
+static void set_size(Object *obj, Visitor *v, const char *name, void *opaque,
+                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index b343a4d..aa0792a 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -887,8 +887,9 @@ char *qdev_get_dev_path(DeviceState *dev)
  * Legacy property handling
  */

-static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
+static void qdev_get_legacy_property(Object *obj, Visitor *v,
+                                     const char *name, void *opaque,
+                                     Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
     Property *prop = opaque;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 0ec39dc..b43da29 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1754,8 +1754,9 @@ static HotplugHandler *pc_get_hotpug_handler(MachineState *machine,
 }

 static void
-pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque,
-                                          const char *name, Error **errp)
+pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v,
+                                          const char *name, void *opaque,
+                                          Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);
     int64_t value = memory_region_size(&pcms->hotplug_memory.mr);
@@ -1764,8 +1765,8 @@ pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque,
 }

 static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
-                                         void *opaque, const char *name,
-                                         Error **errp)
+                                            const char *name, void *opaque,
+                                            Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);
     uint64_t value = pcms->max_ram_below_4g;
@@ -1774,8 +1775,8 @@ static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
 }

 static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
-                                         void *opaque, const char *name,
-                                         Error **errp)
+                                            const char *name, void *opaque,
+                                            Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);
     Error *error = NULL;
@@ -1803,8 +1804,8 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
     pcms->max_ram_below_4g = value;
 }

-static void pc_machine_get_vmport(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);
     OnOffAuto vmport = pcms->vmport;
@@ -1812,8 +1813,8 @@ static void pc_machine_get_vmport(Object *obj, Visitor *v, void *opaque,
     visit_type_OnOffAuto(v, name, &vmport, errp);
 }

-static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void pc_machine_set_vmport(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);

@@ -1845,8 +1846,8 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms)
     return false;
 }

-static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
+static void pc_machine_get_smm(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);
     OnOffAuto smm = pcms->smm;
@@ -1854,8 +1855,8 @@ static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque,
     visit_type_OnOffAuto(v, name, &smm, errp);
 }

-static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void pc_machine_set_smm(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     PCMachineState *pcms = PC_MACHINE(obj);

diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 66f56e7..ec84937 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -199,16 +199,16 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
     return 0;
 }

-static void ide_dev_get_bootindex(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void ide_dev_get_bootindex(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     IDEDevice *d = IDE_DEVICE(obj);

     visit_type_int32(v, name, &d->conf.bootindex, errp);
 }

-static void ide_dev_set_bootindex(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void ide_dev_set_bootindex(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     IDEDevice *d = IDE_DEVICE(obj);
     int32_t boot_index;
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 502c87f..ba7c40f 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -88,8 +88,8 @@ static void xics_common_reset(DeviceState *d)
     device_reset(DEVICE(icp->ics));
 }

-static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
-                                  void *opaque, const char *name, Error **errp)
+static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     XICSState *icp = XICS_COMMON(obj);
     int64_t value = icp->nr_irqs;
@@ -97,8 +97,8 @@ static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
     visit_type_int(v, name, &value, errp);
 }

-static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
-                                  void *opaque, const char *name, Error **errp)
+static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     XICSState *icp = XICS_COMMON(obj);
     XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
@@ -122,7 +122,7 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
 }

 static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
-                                     void *opaque, const char *name,
+                                     const char *name, void *opaque,
                                      Error **errp)
 {
     XICSState *icp = XICS_COMMON(obj);
@@ -132,7 +132,7 @@ static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
 }

 static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
-                                     void *opaque, const char *name,
+                                     const char *name, void *opaque,
                                      Error **errp)
 {
     XICSState *icp = XICS_COMMON(obj);
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 358f707..b9ae1da 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -569,9 +569,8 @@ Object *ich9_lpc_find(void)
     return o;
 }

-static void ich9_lpc_get_sci_int(Object *obj, Visitor *v,
-                                 void *opaque, const char *name,
-                                 Error **errp)
+static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj);
     uint32_t value = ich9_lpc_sci_irq(lpc);
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 123baeb..c52bf09 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -372,8 +372,8 @@ static Property pc_dimm_properties[] = {
     DEFINE_PROP_END_OF_LIST(),
 };

-static void pc_dimm_get_size(Object *obj, Visitor *v, void *opaque,
-                          const char *name, Error **errp)
+static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
 {
     int64_t value;
     MemoryRegion *mr;
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index 4a3dc76..b6c83ce 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -362,8 +362,8 @@ static void pci_edu_uninit(PCIDevice *pdev)
     timer_del(&edu->dma_timer);
 }

-static void edu_obj_uint64(Object *obj, Visitor *v, void *opaque,
-                           const char *name, Error **errp)
+static void edu_obj_uint64(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
 {
     uint64_t *val = opaque;

diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c
index 1850315..a9ac524 100644
--- a/hw/misc/tmp105.c
+++ b/hw/misc/tmp105.c
@@ -52,8 +52,8 @@ static void tmp105_alarm_update(TMP105State *s)
     tmp105_interrupt_update(s);
 }

-static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     TMP105State *s = TMP105(obj);
     int64_t value = s->temperature * 1000 / 256;
@@ -64,8 +64,8 @@ static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
 /* Units are 0.001 centigrades relative to 0 C.  s->temperature is 8.8
  * fixed point, so units are 1/256 centigrades.  A simple ratio will do.
  */
-static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     TMP105State *s = TMP105(obj);
     Error *local_err = NULL;
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 46ef304..630cab3 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -93,8 +93,9 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data)
     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
 }

-static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
+static void isa_ne2000_get_bootindex(Object *obj, Visitor *v,
+                                     const char *name, void *opaque,
+                                     Error **errp)
 {
     ISANE2000State *isa = ISA_NE2000(obj);
     NE2000State *s = &isa->ne2000;
@@ -102,8 +103,9 @@ static void isa_ne2000_get_bootindex(Object *obj, Visitor *v, void *opaque,
     visit_type_int32(v, name, &s->c.bootindex, errp);
 }

-static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
+static void isa_ne2000_set_bootindex(Object *obj, Visitor *v,
+                                     const char *name, void *opaque,
+                                     Error **errp)
 {
     ISANE2000State *isa = ISA_NE2000(obj);
     NE2000State *s = &isa->ne2000;
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index dbae0aa..b5f59af 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -215,7 +215,7 @@ static const VMStateDescription vmstate_i440fx = {
 };

 static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v,
-                                              void *opaque, const char *name,
+                                              const char *name, void *opaque,
                                               Error **errp)
 {
     I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
@@ -225,7 +225,7 @@ static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v,
 }

 static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
-                                            void *opaque, const char *name,
+                                            const char *name, void *opaque,
                                             Error **errp)
 {
     I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
@@ -235,8 +235,8 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
 }

 static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
-                                                void *opaque, const char *name,
-                                                Error **errp)
+                                                const char *name,
+                                                void *opaque, Error **errp)
 {
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
     Range w64;
@@ -247,7 +247,7 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
 }

 static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
-                                              void *opaque, const char *name,
+                                              const char *name, void *opaque,
                                               Error **errp)
 {
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 2b3608a..23b78cc 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -67,7 +67,7 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
 }

 static void q35_host_get_pci_hole_start(Object *obj, Visitor *v,
-                                        void *opaque, const char *name,
+                                        const char *name, void *opaque,
                                         Error **errp)
 {
     Q35PCIHost *s = Q35_HOST_DEVICE(obj);
@@ -77,7 +77,7 @@ static void q35_host_get_pci_hole_start(Object *obj, Visitor *v,
 }

 static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
-                                      void *opaque, const char *name,
+                                      const char *name, void *opaque,
                                       Error **errp)
 {
     Q35PCIHost *s = Q35_HOST_DEVICE(obj);
@@ -87,7 +87,7 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
 }

 static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
-                                          void *opaque, const char *name,
+                                          const char *name, void *opaque,
                                           Error **errp)
 {
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
@@ -99,7 +99,7 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
 }

 static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
-                                        void *opaque, const char *name,
+                                        const char *name, void *opaque,
                                         Error **errp)
 {
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
@@ -110,9 +110,8 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
     visit_type_uint64(v, name, &w64.end, errp);
 }

-static void q35_host_get_mmcfg_size(Object *obj, Visitor *v,
-                                    void *opaque, const char *name,
-                                    Error **errp)
+static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
 {
     PCIExpressHost *e = PCIE_HOST_BRIDGE(obj);
     uint32_t value = e->size;
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 9c8fbe8..9ef603b 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -214,8 +214,8 @@ static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state)
     return RTAS_OUT_SUCCESS;
 }

-static void prop_get_index(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void prop_get_index(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
 {
     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
@@ -223,8 +223,8 @@ static void prop_get_index(Object *obj, Visitor *v, void *opaque,
     visit_type_uint32(v, name, &value, errp);
 }

-static void prop_get_type(Object *obj, Visitor *v, void *opaque,
-                          const char *name, Error **errp)
+static void prop_get_type(Object *obj, Visitor *v, const char *name,
+                          void *opaque, Error **errp)
 {
     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
@@ -239,8 +239,8 @@ static char *prop_get_name(Object *obj, Error **errp)
     return g_strdup(drck->get_name(drc));
 }

-static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void prop_get_entity_sense(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
@@ -250,8 +250,8 @@ static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
     visit_type_uint32(v, name, &value, errp);
 }

-static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
-                        const char *name, Error **errp)
+static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
+                         void *opaque, Error **errp)
 {
     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
     Error *err = NULL;
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 3261446..e743551 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -780,8 +780,8 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data)
     dc->props = msd_properties;
 }

-static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     USBDevice *dev = USB_DEVICE(obj);
     MSDState *s = USB_STORAGE_DEV(dev);
@@ -789,8 +789,8 @@ static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque,
     visit_type_int32(v, name, &s->conf.bootindex, errp);
 }

-static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     USBDevice *dev = USB_DEVICE(obj);
     MSDState *s = USB_STORAGE_DEV(dev);
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 1d81a34..dc44088 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -110,8 +110,8 @@ static void balloon_stats_poll_cb(void *opaque)
     virtio_notify(vdev, s->svq);
 }

-static void balloon_stats_get_all(Object *obj, Visitor *v,
-                                  void *opaque, const char *name, Error **errp)
+static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     Error *err = NULL;
     VirtIOBalloon *s = opaque;
@@ -150,7 +150,7 @@ out:
 }

 static void balloon_stats_get_poll_interval(Object *obj, Visitor *v,
-                                            void *opaque, const char *name,
+                                            const char *name, void *opaque,
                                             Error **errp)
 {
     VirtIOBalloon *s = opaque;
@@ -158,7 +158,7 @@ static void balloon_stats_get_poll_interval(Object *obj, Visitor *v,
 }

 static void balloon_stats_set_poll_interval(Object *obj, Visitor *v,
-                                            void *opaque, const char *name,
+                                            const char *name, void *opaque,
                                             Error **errp)
 {
     VirtIOBalloon *s = opaque;
diff --git a/include/qom/object.h b/include/qom/object.h
index 3e7e99d..698827d 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -290,16 +290,16 @@ typedef struct InterfaceInfo InterfaceInfo;
  * ObjectPropertyAccessor:
  * @obj: the object that owns the property
  * @v: the visitor that contains the property data
+ * @name: the name of the property
  * @opaque: the object property opaque
- * @name: the name of the property
  * @errp: a pointer to an Error that is filled if getting/setting fails.
  *
  * Called when trying to get/set a property.
  */
 typedef void (ObjectPropertyAccessor)(Object *obj,
                                       Visitor *v,
-                                      void *opaque,
                                       const char *name,
+                                      void *opaque,
                                       Error **errp);

 /**
diff --git a/memory.c b/memory.c
index 49eb809..fc58f1c 100644
--- a/memory.c
+++ b/memory.c
@@ -928,8 +928,8 @@ void memory_region_init(MemoryRegion *mr,
     }
 }

-static void memory_region_get_addr(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void memory_region_get_addr(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     MemoryRegion *mr = MEMORY_REGION(obj);
     uint64_t value = mr->addr;
@@ -937,8 +937,9 @@ static void memory_region_get_addr(Object *obj, Visitor *v, void *opaque,
     visit_type_uint64(v, name, &value, errp);
 }

-static void memory_region_get_container(Object *obj, Visitor *v, void *opaque,
-                                        const char *name, Error **errp)
+static void memory_region_get_container(Object *obj, Visitor *v,
+                                        const char *name, void *opaque,
+                                        Error **errp)
 {
     MemoryRegion *mr = MEMORY_REGION(obj);
     gchar *path = (gchar *)"";
@@ -960,8 +961,9 @@ static Object *memory_region_resolve_container(Object *obj, void *opaque,
     return OBJECT(mr->container);
 }

-static void memory_region_get_priority(Object *obj, Visitor *v, void *opaque,
-                                       const char *name, Error **errp)
+static void memory_region_get_priority(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
 {
     MemoryRegion *mr = MEMORY_REGION(obj);
     int32_t value = mr->priority;
@@ -976,8 +978,8 @@ static bool memory_region_get_may_overlap(Object *obj, Error **errp)
     return mr->may_overlap;
 }

-static void memory_region_get_size(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void memory_region_get_size(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     MemoryRegion *mr = MEMORY_REGION(obj);
     uint64_t value = memory_region_size(mr);
diff --git a/net/dump.c b/net/dump.c
index 0aac9b8..760a12d 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -271,8 +271,8 @@ static void filter_dump_setup(NetFilterState *nf, Error **errp)
     net_dump_state_init(&nfds->ds, nfds->filename, nfds->maxlen, errp);
 }

-static void filter_dump_get_maxlen(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void filter_dump_get_maxlen(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     NetFilterDumpState *nfds = FILTER_DUMP(obj);
     uint32_t value = nfds->maxlen;
@@ -280,8 +280,8 @@ static void filter_dump_get_maxlen(Object *obj, Visitor *v, void *opaque,
     visit_type_uint32(v, name, &value, errp);
 }

-static void filter_dump_set_maxlen(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void filter_dump_set_maxlen(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     NetFilterDumpState *nfds = FILTER_DUMP(obj);
     Error *local_err = NULL;
diff --git a/net/filter-buffer.c b/net/filter-buffer.c
index 3173d26..570d342 100644
--- a/net/filter-buffer.c
+++ b/net/filter-buffer.c
@@ -132,8 +132,9 @@ static void filter_buffer_class_init(ObjectClass *oc, void *data)
     nfc->receive_iov = filter_buffer_receive_iov;
 }

-static void filter_buffer_get_interval(Object *obj, Visitor *v, void *opaque,
-                                       const char *name, Error **errp)
+static void filter_buffer_get_interval(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
 {
     FilterBufferState *s = FILTER_BUFFER(obj);
     uint32_t value = s->interval;
@@ -141,8 +142,9 @@ static void filter_buffer_get_interval(Object *obj, Visitor *v, void *opaque,
     visit_type_uint32(v, name, &value, errp);
 }

-static void filter_buffer_set_interval(Object *obj, Visitor *v, void *opaque,
-                                       const char *name, Error **errp)
+static void filter_buffer_set_interval(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
 {
     FilterBufferState *s = FILTER_BUFFER(obj);
     Error *local_err = NULL;
diff --git a/qom/object.c b/qom/object.c
index 6a5ad59..55b3955 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1061,7 +1061,7 @@ void object_property_get(Object *obj, Visitor *v, const char *name,
     if (!prop->get) {
         error_setg(errp, QERR_PERMISSION_DENIED);
     } else {
-        prop->get(obj, v, prop->opaque, name, errp);
+        prop->get(obj, v, name, prop->opaque, errp);
     }
 }

@@ -1076,7 +1076,7 @@ void object_property_set(Object *obj, Visitor *v, const char *name,
     if (!prop->set) {
         error_setg(errp, QERR_PERMISSION_DENIED);
     } else {
-        prop->set(obj, v, prop->opaque, name, errp);
+        prop->set(obj, v, name, prop->opaque, errp);
     }
 }

@@ -1334,8 +1334,9 @@ Object *object_get_objects_root(void)
     return container_get(object_get_root(), "/objects");
 }

-static void object_get_child_property(Object *obj, Visitor *v, void *opaque,
-                                      const char *name, Error **errp)
+static void object_get_child_property(Object *obj, Visitor *v,
+                                      const char *name, void *opaque,
+                                      Error **errp)
 {
     Object *child = opaque;
     gchar *path;
@@ -1403,8 +1404,9 @@ typedef struct {
     ObjectPropertyLinkFlags flags;
 } LinkProperty;

-static void object_get_link_property(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
+static void object_get_link_property(Object *obj, Visitor *v,
+                                     const char *name, void *opaque,
+                                     Error **errp)
 {
     LinkProperty *lprop = opaque;
     Object **child = lprop->child;
@@ -1460,8 +1462,9 @@ static Object *object_resolve_link(Object *obj, const char *name,
     return target;
 }

-static void object_set_link_property(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
+static void object_set_link_property(Object *obj, Visitor *v,
+                                     const char *name, void *opaque,
+                                     Error **errp)
 {
     Error *local_err = NULL;
     LinkProperty *prop = opaque;
@@ -1724,8 +1727,8 @@ typedef struct StringProperty
     void (*set)(Object *, const char *, Error **);
 } StringProperty;

-static void property_get_str(Object *obj, Visitor *v, void *opaque,
-                             const char *name, Error **errp)
+static void property_get_str(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
 {
     StringProperty *prop = opaque;
     char *value;
@@ -1741,8 +1744,8 @@ static void property_get_str(Object *obj, Visitor *v, void *opaque,
     g_free(value);
 }

-static void property_set_str(Object *obj, Visitor *v, void *opaque,
-                             const char *name, Error **errp)
+static void property_set_str(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
 {
     StringProperty *prop = opaque;
     char *value;
@@ -1816,8 +1819,8 @@ typedef struct BoolProperty
     void (*set)(Object *, bool, Error **);
 } BoolProperty;

-static void property_get_bool(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
+static void property_get_bool(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
 {
     BoolProperty *prop = opaque;
     bool value;
@@ -1832,8 +1835,8 @@ static void property_get_bool(Object *obj, Visitor *v, void *opaque,
     visit_type_bool(v, name, &value, errp);
 }

-static void property_set_bool(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
+static void property_set_bool(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
 {
     BoolProperty *prop = opaque;
     bool value;
@@ -1899,8 +1902,8 @@ void object_class_property_add_bool(ObjectClass *klass, const char *name,
     }
 }

-static void property_get_enum(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
+static void property_get_enum(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
 {
     EnumProperty *prop = opaque;
     int value;
@@ -1915,8 +1918,8 @@ static void property_get_enum(Object *obj, Visitor *v, void *opaque,
     visit_type_enum(v, name, &value, prop->strings, NULL, errp);
 }

-static void property_set_enum(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
+static void property_set_enum(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
 {
     EnumProperty *prop = opaque;
     int value;
@@ -1991,8 +1994,8 @@ typedef struct TMProperty {
     void (*get)(Object *, struct tm *, Error **);
 } TMProperty;

-static void property_get_tm(Object *obj, Visitor *v, void *opaque,
-                            const char *name, Error **errp)
+static void property_get_tm(Object *obj, Visitor *v, const char *name,
+                            void *opaque, Error **errp)
 {
     TMProperty *prop = opaque;
     Error *err = NULL;
@@ -2090,33 +2093,29 @@ static char *qdev_get_type(Object *obj, Error **errp)
     return g_strdup(object_get_typename(obj));
 }

-static void property_get_uint8_ptr(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     uint8_t value = *(uint8_t *)opaque;
     visit_type_uint8(v, name, &value, errp);
 }

-static void property_get_uint16_ptr(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
 {
     uint16_t value = *(uint16_t *)opaque;
     visit_type_uint16(v, name, &value, errp);
 }

-static void property_get_uint32_ptr(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
 {
     uint32_t value = *(uint32_t *)opaque;
     visit_type_uint32(v, name, &value, errp);
 }

-static void property_get_uint64_ptr(Object *obj, Visitor *v,
-                                   void *opaque, const char *name,
-                                   Error **errp)
+static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
 {
     uint64_t value = *(uint64_t *)opaque;
     visit_type_uint64(v, name, &value, errp);
@@ -2183,16 +2182,16 @@ typedef struct {
     char *target_name;
 } AliasProperty;

-static void property_get_alias(Object *obj, Visitor *v, void *opaque,
-                               const char *name, Error **errp)
+static void property_get_alias(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     AliasProperty *prop = opaque;

     object_property_get(prop->target_obj, v, prop->target_name, errp);
 }

-static void property_set_alias(Object *obj, Visitor *v, void *opaque,
-                               const char *name, Error **errp)
+static void property_set_alias(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     AliasProperty *prop = opaque;

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 110fa1c..e86711b 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1509,8 +1509,9 @@ static void report_unavailable_features(FeatureWord w, uint32_t mask)
     }
 }

-static void x86_cpuid_version_get_family(Object *obj, Visitor *v, void *opaque,
-                                         const char *name, Error **errp)
+static void x86_cpuid_version_get_family(Object *obj, Visitor *v,
+                                         const char *name, void *opaque,
+                                         Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     CPUX86State *env = &cpu->env;
@@ -1523,8 +1524,9 @@ static void x86_cpuid_version_get_family(Object *obj, Visitor *v, void *opaque,
     visit_type_int(v, name, &value, errp);
 }

-static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque,
-                                         const char *name, Error **errp)
+static void x86_cpuid_version_set_family(Object *obj, Visitor *v,
+                                         const char *name, void *opaque,
+                                         Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     CPUX86State *env = &cpu->env;
@@ -1552,8 +1554,9 @@ static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque,
     }
 }

-static void x86_cpuid_version_get_model(Object *obj, Visitor *v, void *opaque,
-                                        const char *name, Error **errp)
+static void x86_cpuid_version_get_model(Object *obj, Visitor *v,
+                                        const char *name, void *opaque,
+                                        Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     CPUX86State *env = &cpu->env;
@@ -1564,8 +1567,9 @@ static void x86_cpuid_version_get_model(Object *obj, Visitor *v, void *opaque,
     visit_type_int(v, name, &value, errp);
 }

-static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque,
-                                        const char *name, Error **errp)
+static void x86_cpuid_version_set_model(Object *obj, Visitor *v,
+                                        const char *name, void *opaque,
+                                        Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     CPUX86State *env = &cpu->env;
@@ -1590,7 +1594,7 @@ static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque,
 }

 static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v,
-                                           void *opaque, const char *name,
+                                           const char *name, void *opaque,
                                            Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
@@ -1602,7 +1606,7 @@ static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v,
 }

 static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v,
-                                           void *opaque, const char *name,
+                                           const char *name, void *opaque,
                                            Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
@@ -1698,8 +1702,8 @@ static void x86_cpuid_set_model_id(Object *obj, const char *model_id,
     }
 }

-static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     int64_t value;
@@ -1708,8 +1712,8 @@ static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, void *opaque,
     visit_type_int(v, name, &value, errp);
 }

-static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
+static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     const int64_t min = 0;
@@ -1731,8 +1735,8 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
     cpu->env.tsc_khz = value / 1000;
 }

-static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     int64_t value = cpu->apic_id;
@@ -1740,8 +1744,8 @@ static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque,
     visit_type_int(v, name, &value, errp);
 }

-static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque,
-                                  const char *name, Error **errp)
+static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     DeviceState *dev = DEVICE(obj);
@@ -1776,8 +1780,9 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque,
 }

 /* Generic getter for "feature-words" and "filtered-features" properties */
-static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque,
-                                      const char *name, Error **errp)
+static void x86_cpu_get_feature_words(Object *obj, Visitor *v,
+                                      const char *name, void *opaque,
+                                      Error **errp)
 {
     uint32_t *array = (uint32_t *)opaque;
     FeatureWord w;
@@ -1805,8 +1810,8 @@ static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque,
     error_propagate(errp, err);
 }

-static void x86_get_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
+static void x86_get_hv_spinlocks(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     X86CPU *cpu = X86_CPU(obj);
     int64_t value = cpu->hyperv_spinlock_attempts;
@@ -1814,8 +1819,8 @@ static void x86_get_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
     visit_type_int(v, name, &value, errp);
 }

-static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
+static void x86_set_hv_spinlocks(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     const int64_t min = 0xFFF;
     const int64_t max = UINT_MAX;
@@ -2920,21 +2925,16 @@ typedef struct BitProperty {
     uint32_t mask;
 } BitProperty;

-static void x86_cpu_get_bit_prop(Object *obj,
-                                 Visitor *v,
-                                 void *opaque,
-                                 const char *name,
-                                 Error **errp)
+static void x86_cpu_get_bit_prop(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     BitProperty *fp = opaque;
     bool value = (*fp->ptr & fp->mask) == fp->mask;
     visit_type_bool(v, name, &value, errp);
 }

-static void x86_cpu_set_bit_prop(Object *obj,
-                                 Visitor *v,
-                                 void *opaque,
-                                 const char *name,
+static void x86_cpu_set_bit_prop(Object *obj, struct Visitor *v,
+                                 const char *name, void *opaque,
                                  Error **errp)
 {
     DeviceState *dev = DEVICE(obj);
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 9d7936b..d36b627 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8034,8 +8034,8 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
     pcc->l1_icache_size = 0x10000;
 }

-static void powerpc_get_compat(Object *obj, Visitor *v,
-                               void *opaque, const char *name, Error **errp)
+static void powerpc_get_compat(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     char *value = (char *)"";
     Property *prop = opaque;
@@ -8062,8 +8062,8 @@ static void powerpc_get_compat(Object *obj, Visitor *v,
     visit_type_str(v, name, &value, errp);
 }

-static void powerpc_set_compat(Object *obj, Visitor *v,
-                               void *opaque, const char *name, Error **errp)
+static void powerpc_set_compat(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
 {
     Error *error = NULL;
     char *value = NULL;
diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
index e728611..07c1823 100644
--- a/tests/test-qdev-global-props.c
+++ b/tests/test-qdev-global-props.c
@@ -116,22 +116,16 @@ static void test_static_globalprop(void)
 #define TYPE_UNUSED_HOTPLUG   "hotplug-type"
 #define TYPE_UNUSED_NOHOTPLUG "nohotplug-type"

-static void prop1_accessor(Object *obj,
-                           Visitor *v,
-                           void *opaque,
-                           const char *name,
-                           Error **errp)
+static void prop1_accessor(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
 {
     MyType *mt = DYNAMIC_TYPE(obj);

     visit_type_uint32(v, name, &mt->prop1, errp);
 }

-static void prop2_accessor(Object *obj,
-                           Visitor *v,
-                           void *opaque,
-                           const char *name,
-                           Error **errp)
+static void prop2_accessor(Object *obj, Visitor *v, const char *name,
+                           void *opaque, Error **errp)
 {
     MyType *mt = DYNAMIC_TYPE(obj);

-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (14 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 18:55   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
                   ` (21 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

As explained in the previous patches, matching argument order of
'name, &value' to JSON's "name":value makes sense.  However,
while the last two patches were easy with Coccinelle, I ended up
doing this one all by hand.  Now all the visitor callbacks match
the main interface.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: new patch
---
 include/qapi/visitor-impl.h  | 39 +++++++++++++++++++++------------------
 qapi/opts-visitor.c          | 16 ++++++++--------
 qapi/qapi-dealloc-visitor.c  | 25 ++++++++++++-------------
 qapi/qapi-visit-core.c       | 38 +++++++++++++++++++-------------------
 qapi/qmp-input-visitor.c     | 22 +++++++++++-----------
 qapi/qmp-output-visitor.c    | 16 ++++++++--------
 qapi/string-input-visitor.c  | 16 ++++++++--------
 qapi/string-output-visitor.c | 16 ++++++++--------
 8 files changed, 95 insertions(+), 93 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index e6399d1..0257359 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -18,8 +18,8 @@
 struct Visitor
 {
     /* Must be set */
-    void (*start_struct)(Visitor *v, void **obj, const char *kind,
-                         const char *name, size_t size, Error **errp);
+    void (*start_struct)(Visitor *v, const char *name, void **obj,
+                         const char *kind, size_t size, Error **errp);
     void (*end_struct)(Visitor *v, Error **errp);

     void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
@@ -30,39 +30,42 @@ struct Visitor
     GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
     void (*end_list)(Visitor *v, Error **errp);

-    void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
-                      const char *kind, const char *name, Error **errp);
+    void (*type_enum)(Visitor *v, const char *name, int *obj,
+                      const char * const strings[], const char *kind,
+                      Error **errp);
     /* May be NULL; only needed for input visitors. */
-    void (*get_next_type)(Visitor *v, QType *type, bool promote_int,
-                          const char *name, Error **errp);
+    void (*get_next_type)(Visitor *v, const char *name, QType *type,
+                          bool promote_int, Error **errp);

     /* Must be set. */
-    void (*type_int64)(Visitor *v, int64_t *obj, const char *name,
+    void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
                        Error **errp);
     /* Must be set. */
-    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name,
+    void (*type_uint64)(Visitor *v, const char *name, uint64_t *obj,
                         Error **errp);
     /* Optional; fallback is type_uint64().  */
-    void (*type_size)(Visitor *v, uint64_t *obj, const char *name,
+    void (*type_size)(Visitor *v, const char *name, uint64_t *obj,
                       Error **errp);
     /* Must be set. */
-    void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
-    void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
-    void (*type_number)(Visitor *v, double *obj, const char *name,
+    void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp);
+    void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp);
+    void (*type_number)(Visitor *v, const char *name, double *obj,
                         Error **errp);
-    void (*type_any)(Visitor *v, QObject **obj, const char *name,
+    void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);

     /* May be NULL; most useful for input visitors. */
-    void (*optional)(Visitor *v, bool *present, const char *name);
+    void (*optional)(Visitor *v, const char *name, bool *present);

     bool (*start_union)(Visitor *v, bool data_present, Error **errp);
     void (*end_union)(Visitor *v, bool data_present, Error **errp);
 };

-void input_type_enum(Visitor *v, int *obj, const char * const strings[],
-                     const char *kind, const char *name, Error **errp);
-void output_type_enum(Visitor *v, int *obj, const char * const strings[],
-                      const char *kind, const char *name, Error **errp);
+void input_type_enum(Visitor *v, const char *name, int *obj,
+                     const char * const strings[], const char *kind,
+                     Error **errp);
+void output_type_enum(Visitor *v, const char *name, int *obj,
+                      const char * const strings[], const char *kind,
+                      Error **errp);

 #endif
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 56c798f..b1c8e50 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -124,8 +124,8 @@ opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)


 static void
-opts_start_struct(Visitor *v, void **obj, const char *kind,
-                  const char *name, size_t size, Error **errp)
+opts_start_struct(Visitor *v, const char *name, void **obj, const char *kind,
+                  size_t size, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -311,7 +311,7 @@ processed(OptsVisitor *ov, const char *name)


 static void
-opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
+opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -327,7 +327,7 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)

 /* mimics qemu-option.c::parse_option_bool() */
 static void
-opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
+opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -360,7 +360,7 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)


 static void
-opts_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
+opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -416,7 +416,7 @@ opts_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)


 static void
-opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -468,7 +468,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)


 static void
-opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
@@ -494,7 +494,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)


 static void
-opts_optional(Visitor *v, bool *present, const char *name)
+opts_optional(Visitor *v, const char *name, bool *present)
 {
     OptsVisitor *ov = to_ov(v);

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 11eb828..b521f7c 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -58,8 +58,8 @@ static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
     return value;
 }

-static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind,
-                                      const char *name, size_t unused,
+static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
+                                      const char *kind, size_t unused,
                                       Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
@@ -127,7 +127,7 @@ static void qapi_dealloc_end_list(Visitor *v, Error **errp)
     assert(obj == NULL); /* should've been list head tracker with no payload */
 }

-static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
+static void qapi_dealloc_type_str(Visitor *v, const char *name, char **obj,
                                   Error **errp)
 {
     if (obj) {
@@ -135,38 +135,37 @@ static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
     }
 }

-static void qapi_dealloc_type_int64(Visitor *v, int64_t *obj, const char *name,
+static void qapi_dealloc_type_int64(Visitor *v, const char *name, int64_t *obj,
                                     Error **errp)
 {
 }

-static void qapi_dealloc_type_uint64(Visitor *v, uint64_t *obj,
-                                     const char *name, Error **errp)
+static void qapi_dealloc_type_uint64(Visitor *v, const char *name,
+                                     uint64_t *obj, Error **errp)
 {
 }

-static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name,
+static void qapi_dealloc_type_bool(Visitor *v, const char *name, bool *obj,
                                    Error **errp)
 {
 }

-static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
+static void qapi_dealloc_type_number(Visitor *v, const char *name, double *obj,
                                      Error **errp)
 {
 }

-static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
-                                       const char *name, Error **errp)
+static void qapi_dealloc_type_anything(Visitor *v, const char *name,
+                                       QObject **obj, Error **errp)
 {
     if (obj) {
         qobject_decref(*obj);
     }
 }

-static void qapi_dealloc_type_enum(Visitor *v, int *obj,
+static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
                                    const char * const strings[],
-                                   const char *kind, const char *name,
-                                   Error **errp)
+                                   const char *kind, Error **errp)
 {
 }

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 609db22..7bc3de6 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -20,7 +20,7 @@
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         const char *kind, size_t size, Error **errp)
 {
-    v->start_struct(v, obj, kind, name, size, errp);
+    v->start_struct(v, name, obj, kind, size, errp);
 }

 void visit_end_struct(Visitor *v, Error **errp)
@@ -76,7 +76,7 @@ void visit_end_union(Visitor *v, bool data_present, Error **errp)
 bool visit_optional(Visitor *v, const char *name, bool *present)
 {
     if (v->optional) {
-        v->optional(v, present, name);
+        v->optional(v, name, present);
     }
     return *present;
 }
@@ -85,7 +85,7 @@ void visit_get_next_type(Visitor *v, const char *name, QType *type,
                          bool promote_int, Error **errp)
 {
     if (v->get_next_type) {
-        v->get_next_type(v, type, promote_int, name, errp);
+        v->get_next_type(v, name, type, promote_int, errp);
     }
 }

@@ -93,12 +93,12 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], const char *kind,
                      Error **errp)
 {
-    v->type_enum(v, obj, strings, kind, name, errp);
+    v->type_enum(v, name, obj, strings, kind, errp);
 }

 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
 {
-    v->type_int64(v, obj, name, errp);
+    v->type_int64(v, name, obj, errp);
 }

 static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name,
@@ -107,7 +107,7 @@ static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name,
     Error *err = NULL;
     uint64_t value = *obj;

-    v->type_uint64(v, &value, name, &err);
+    v->type_uint64(v, name, &value, &err);
     if (err) {
         error_propagate(errp, err);
     } else if (value > max) {
@@ -145,7 +145,7 @@ void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
 void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                        Error **errp)
 {
-    v->type_uint64(v, obj, name, errp);
+    v->type_uint64(v, name, obj, errp);
 }

 static void visit_type_intN(Visitor *v, int64_t *obj, const char *name,
@@ -155,7 +155,7 @@ static void visit_type_intN(Visitor *v, int64_t *obj, const char *name,
     Error *err = NULL;
     int64_t value = *obj;

-    v->type_int64(v, &value, name, &err);
+    v->type_int64(v, name, &value, &err);
     if (err) {
         error_propagate(errp, err);
     } else if (value < min || value > max) {
@@ -192,42 +192,42 @@ void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
 void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
                       Error **errp)
 {
-    v->type_int64(v, obj, name, errp);
+    v->type_int64(v, name, obj, errp);
 }

 void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
                      Error **errp)
 {
     if (v->type_size) {
-        v->type_size(v, obj, name, errp);
+        v->type_size(v, name, obj, errp);
     } else {
-        v->type_uint64(v, obj, name, errp);
+        v->type_uint64(v, name, obj, errp);
     }
 }

 void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
 {
-    v->type_bool(v, obj, name, errp);
+    v->type_bool(v, name, obj, errp);
 }

 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
-    v->type_str(v, obj, name, errp);
+    v->type_str(v, name, obj, errp);
 }

 void visit_type_number(Visitor *v, const char *name, double *obj,
                        Error **errp)
 {
-    v->type_number(v, obj, name, errp);
+    v->type_number(v, name, obj, errp);
 }

 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 {
-    v->type_any(v, obj, name, errp);
+    v->type_any(v, name, obj, errp);
 }

-void output_type_enum(Visitor *v, int *obj, const char * const strings[],
-                      const char *kind, const char *name,
+void output_type_enum(Visitor *v, const char *name, int *obj,
+                      const char * const strings[], const char *kind,
                       Error **errp)
 {
     int i = 0;
@@ -245,8 +245,8 @@ void output_type_enum(Visitor *v, int *obj, const char * const strings[],
     visit_type_str(v, name, &enum_str, errp);
 }

-void input_type_enum(Visitor *v, int *obj, const char * const strings[],
-                     const char *kind, const char *name,
+void input_type_enum(Visitor *v, const char *name, int *obj,
+                     const char * const strings[], const char *kind,
                      Error **errp)
 {
     Error *local_err = NULL;
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 32b60bb..de77740 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -114,8 +114,8 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
     qiv->nb_stack--;
 }

-static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind,
-                                   const char *name, size_t size, Error **errp)
+static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
+                                   const char *kind, size_t size, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
@@ -208,8 +208,8 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
     qmp_input_pop(qiv, errp);
 }

-static void qmp_input_get_next_type(Visitor *v, QType *type, bool promote_int,
-                                    const char *name, Error **errp)
+static void qmp_input_get_next_type(Visitor *v, const char *name, QType *type,
+                                    bool promote_int, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, false);
@@ -224,7 +224,7 @@ static void qmp_input_get_next_type(Visitor *v, QType *type, bool promote_int,
     }
 }

-static void qmp_input_type_int64(Visitor *v, int64_t *obj, const char *name,
+static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj,
                                  Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -239,7 +239,7 @@ static void qmp_input_type_int64(Visitor *v, int64_t *obj, const char *name,
     *obj = qint_get_int(qint);
 }

-static void qmp_input_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                                   Error **errp)
 {
     /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
@@ -255,7 +255,7 @@ static void qmp_input_type_uint64(Visitor *v, uint64_t *obj, const char *name,
     *obj = qint_get_int(qint);
 }

-static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
+static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
                                 Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -270,7 +270,7 @@ static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name,
     *obj = qbool_get_bool(qbool);
 }

-static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
+static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
                                Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -285,7 +285,7 @@ static void qmp_input_type_str(Visitor *v, char **obj, const char *name,
     *obj = g_strdup(qstring_get_str(qstr));
 }

-static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
+static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
                                   Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -309,7 +309,7 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
                "number");
 }

-static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
+static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
                                Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -319,7 +319,7 @@ static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
     *obj = qobj;
 }

-static void qmp_input_optional(Visitor *v, bool *present, const char *name)
+static void qmp_input_optional(Visitor *v, const char *name, bool *present)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index f8eebaa..af600a9 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -110,8 +110,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
     }
 }

-static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind,
-                                    const char *name, size_t unused,
+static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
+                                    const char *kind, size_t unused,
                                     Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
@@ -158,14 +158,14 @@ static void qmp_output_end_list(Visitor *v, Error **errp)
     qmp_output_pop(qov);
 }

-static void qmp_output_type_int64(Visitor *v, int64_t *obj, const char *name,
+static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj,
                                   Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     qmp_output_add(qov, name, qint_from_int(*obj));
 }

-static void qmp_output_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+static void qmp_output_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                                    Error **errp)
 {
     /* FIXME: QMP outputs values larger than INT64_MAX as negative */
@@ -173,14 +173,14 @@ static void qmp_output_type_uint64(Visitor *v, uint64_t *obj, const char *name,
     qmp_output_add(qov, name, qint_from_int(*obj));
 }

-static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name,
+static void qmp_output_type_bool(Visitor *v, const char *name, bool *obj,
                                  Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     qmp_output_add(qov, name, qbool_from_bool(*obj));
 }

-static void qmp_output_type_str(Visitor *v, char **obj, const char *name,
+static void qmp_output_type_str(Visitor *v, const char *name, char **obj,
                                 Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
@@ -191,14 +191,14 @@ static void qmp_output_type_str(Visitor *v, char **obj, const char *name,
     }
 }

-static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
+static void qmp_output_type_number(Visitor *v, const char *name, double *obj,
                                    Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     qmp_output_add(qov, name, qfloat_from_double(*obj));
 }

-static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
+static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
                                 Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index d7546b5..5347b61 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -184,7 +184,7 @@ end_list(Visitor *v, Error **errp)
     siv->head = true;
 }

-static void parse_type_int64(Visitor *v, int64_t *obj, const char *name,
+static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
                              Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
@@ -226,13 +226,13 @@ error:
                "an int64 value or range");
 }

-static void parse_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                               Error **errp)
 {
     /* FIXME: parse_type_int64 mishandles values over INT64_MAX */
     int64_t i;
     Error *err = NULL;
-    parse_type_int64(v, &i, name, &err);
+    parse_type_int64(v, name, &i, &err);
     if (err) {
         error_propagate(errp, err);
     } else {
@@ -240,7 +240,7 @@ static void parse_type_uint64(Visitor *v, uint64_t *obj, const char *name,
     }
 }

-static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
+static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
                             Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
@@ -262,7 +262,7 @@ static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
     *obj = val;
 }

-static void parse_type_bool(Visitor *v, bool *obj, const char *name,
+static void parse_type_bool(Visitor *v, const char *name, bool *obj,
                             Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
@@ -286,7 +286,7 @@ static void parse_type_bool(Visitor *v, bool *obj, const char *name,
                "boolean");
 }

-static void parse_type_str(Visitor *v, char **obj, const char *name,
+static void parse_type_str(Visitor *v, const char *name, char **obj,
                            Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
@@ -298,7 +298,7 @@ static void parse_type_str(Visitor *v, char **obj, const char *name,
     }
 }

-static void parse_type_number(Visitor *v, double *obj, const char *name,
+static void parse_type_number(Visitor *v, const char *name, double *obj,
                               Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
@@ -318,7 +318,7 @@ static void parse_type_number(Visitor *v, double *obj, const char *name,
     *obj = val;
 }

-static void parse_optional(Visitor *v, bool *present, const char *name)
+static void parse_optional(Visitor *v, const char *name, bool *present)
 {
     StringInputVisitor *siv = to_siv(v);

diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 3ed2b2c..74de6b6 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -121,7 +121,7 @@ static void format_string(StringOutputVisitor *sov, Range *r, bool next,
     }
 }

-static void print_type_int64(Visitor *v, int64_t *obj, const char *name,
+static void print_type_int64(Visitor *v, const char *name, int64_t *obj,
                              Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
@@ -197,16 +197,16 @@ static void print_type_int64(Visitor *v, int64_t *obj, const char *name,
     }
 }

-static void print_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+static void print_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                              Error **errp)
 {
     /* FIXME: print_type_int64 mishandles values over INT64_MAX */
     int64_t i = *obj;
-    print_type_int64(v, &i, name, errp);
+    print_type_int64(v, name, &i, errp);
 }

-static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
-                           Error **errp)
+static void print_type_size(Visitor *v, const char *name, uint64_t *obj,
+                            Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
     static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' };
@@ -236,14 +236,14 @@ static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
     string_output_set(sov, out);
 }

-static void print_type_bool(Visitor *v, bool *obj, const char *name,
+static void print_type_bool(Visitor *v, const char *name, bool *obj,
                             Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
 }

-static void print_type_str(Visitor *v, char **obj, const char *name,
+static void print_type_str(Visitor *v, const char *name, char **obj,
                            Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
@@ -257,7 +257,7 @@ static void print_type_str(Visitor *v, char **obj, const char *name,
     string_output_set(sov, out);
 }

-static void print_type_number(Visitor *v, double *obj, const char *name,
+static void print_type_number(Visitor *v, const char *name, double *obj,
                               Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (15 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 18:59   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct Eric Blake
                   ` (20 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, Michael S. Tsirkin, Alexander Graf, armbru,
	open list:sPAPR, Paolo Bonzini, marcandre.lureau,
	Luiz Capitulino, Andreas Färber, David Gibson

visit_start_struct() and visit_type_enum() had a 'kind' argument
that was usually set to either the stringized version of the
corresponding qapi type name, or to NULL (although some clients
didn't even get that right).  But nothing ever used the argument.
It's even hard to argue that it would be useful in a debugger,
as a stack backtrace also tells which type is being visited.

Therefore, drop the 'kind' argument as dead.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to 'name' motion
v7: new patch
---
 hmp.c                       |  2 +-
 hw/core/qdev-properties.c   |  6 ++----
 hw/ppc/spapr_drc.c          |  4 ++--
 hw/virtio/virtio-balloon.c  |  4 ++--
 include/qapi/visitor-impl.h | 11 ++++-------
 include/qapi/visitor.h      |  5 ++---
 qapi/opts-visitor.c         |  2 +-
 qapi/qapi-dealloc-visitor.c |  6 ++----
 qapi/qapi-visit-core.c      | 16 +++++++---------
 qapi/qmp-input-visitor.c    |  2 +-
 qapi/qmp-output-visitor.c   |  3 +--
 qom/object.c                |  8 ++++----
 scripts/qapi-event.py       |  2 +-
 scripts/qapi-visit.py       | 12 ++++++------
 vl.c                        |  2 +-
 15 files changed, 37 insertions(+), 48 deletions(-)

diff --git a/hmp.c b/hmp.c
index 3beea3a..a4b74df 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1669,7 +1669,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
     pdict = qdict_clone_shallow(qdict);
     v = opts_get_visitor(ov);

-    visit_start_struct(v, NULL, NULL, NULL, 0, &err);
+    visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out_clean;
     }
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index de927c6..e7c7719 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -48,8 +48,7 @@ static void get_enum(Object *obj, Visitor *v, const char *name, void *opaque,
     Property *prop = opaque;
     int *ptr = qdev_get_prop_ptr(dev, prop);

-    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
-                    prop->info->name, errp);
+    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
 }

 static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
@@ -64,8 +63,7 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque,
         return;
     }

-    visit_type_enum(v, prop->name, ptr, prop->info->enum_table,
-                    prop->info->name, errp);
+    visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp);
 }

 /* Bit */
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 9ef603b..78d71e2 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -259,7 +259,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
     void *fdt;

     if (!drc->fdt) {
-        visit_start_struct(v, name, NULL, NULL, 0, &err);
+        visit_start_struct(v, name, NULL, 0, &err);
         if (!err) {
             visit_end_struct(v, &err);
         }
@@ -282,7 +282,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
         case FDT_BEGIN_NODE:
             fdt_depth++;
             name = fdt_get_name(fdt, fdt_offset, &name_len);
-            visit_start_struct(v, name, NULL, NULL, 0, &err);
+            visit_start_struct(v, name, NULL, 0, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index dc44088..24ecd87 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -117,7 +117,7 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
     VirtIOBalloon *s = opaque;
     int i;

-    visit_start_struct(v, name, NULL, "guest-stats", 0, &err);
+    visit_start_struct(v, name, NULL, 0, &err);
     if (err) {
         goto out;
     }
@@ -126,7 +126,7 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
         goto out_end;
     }

-    visit_start_struct(v, "stats", NULL, NULL, 0, &err);
+    visit_start_struct(v, "stats", NULL, 0, &err);
     if (err) {
         goto out_end;
     }
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 0257359..6abfda7 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -19,7 +19,7 @@ struct Visitor
 {
     /* Must be set */
     void (*start_struct)(Visitor *v, const char *name, void **obj,
-                         const char *kind, size_t size, Error **errp);
+                         size_t size, Error **errp);
     void (*end_struct)(Visitor *v, Error **errp);

     void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
@@ -31,8 +31,7 @@ struct Visitor
     void (*end_list)(Visitor *v, Error **errp);

     void (*type_enum)(Visitor *v, const char *name, int *obj,
-                      const char * const strings[], const char *kind,
-                      Error **errp);
+                      const char *const strings[], Error **errp);
     /* May be NULL; only needed for input visitors. */
     void (*get_next_type)(Visitor *v, const char *name, QType *type,
                           bool promote_int, Error **errp);
@@ -62,10 +61,8 @@ struct Visitor
 };

 void input_type_enum(Visitor *v, const char *name, int *obj,
-                     const char * const strings[], const char *kind,
-                     Error **errp);
+                     const char *const strings[], Error **errp);
 void output_type_enum(Visitor *v, const char *name, int *obj,
-                      const char * const strings[], const char *kind,
-                      Error **errp);
+                      const char *const strings[], Error **errp);

 #endif
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index eb50116..4abc180 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -28,7 +28,7 @@ typedef struct GenericList
 } GenericList;

 void visit_start_struct(Visitor *v, const char *name, void **obj,
-                        const char *kind, size_t size, Error **errp);
+                        size_t size, Error **errp);
 void visit_end_struct(Visitor *v, Error **errp);
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
@@ -54,8 +54,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
 void visit_get_next_type(Visitor *v, const char *name, QType *type,
                          bool promote_int, Error **errp);
 void visit_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], const char *kind,
-                     Error **errp);
+                     const char *const strings[], Error **errp);
 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
 void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
                       Error **errp);
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index b1c8e50..6d4a91e 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -124,7 +124,7 @@ opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)


 static void
-opts_start_struct(Visitor *v, const char *name, void **obj, const char *kind,
+opts_start_struct(Visitor *v, const char *name, void **obj,
                   size_t size, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index b521f7c..49e7cf0 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -59,8 +59,7 @@ static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
 }

 static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
-                                      const char *kind, size_t unused,
-                                      Error **errp)
+                                      size_t unused, Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, obj);
@@ -164,8 +163,7 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name,
 }

 static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
-                                   const char * const strings[],
-                                   const char *kind, Error **errp)
+                                   const char * const strings[], Error **errp)
 {
 }

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 7bc3de6..b0452cf 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -2,6 +2,7 @@
  * Core Definitions for QAPI Visitor Classes
  *
  * Copyright IBM, Corp. 2011
+ * Copyright (C) 2015 Red Hat, Inc.
  *
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
@@ -18,9 +19,9 @@
 #include "qapi/visitor-impl.h"

 void visit_start_struct(Visitor *v, const char *name, void **obj,
-                        const char *kind, size_t size, Error **errp)
+                        size_t size, Error **errp)
 {
-    v->start_struct(v, name, obj, kind, size, errp);
+    v->start_struct(v, name, obj, size, errp);
 }

 void visit_end_struct(Visitor *v, Error **errp)
@@ -90,10 +91,9 @@ void visit_get_next_type(Visitor *v, const char *name, QType *type,
 }

 void visit_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], const char *kind,
-                     Error **errp)
+                     const char *const strings[], Error **errp)
 {
-    v->type_enum(v, name, obj, strings, kind, errp);
+    v->type_enum(v, name, obj, strings, errp);
 }

 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
@@ -227,8 +227,7 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 }

 void output_type_enum(Visitor *v, const char *name, int *obj,
-                      const char * const strings[], const char *kind,
-                      Error **errp)
+                      const char * const strings[], Error **errp)
 {
     int i = 0;
     int value = *obj;
@@ -246,8 +245,7 @@ void output_type_enum(Visitor *v, const char *name, int *obj,
 }

 void input_type_enum(Visitor *v, const char *name, int *obj,
-                     const char * const strings[], const char *kind,
-                     Error **errp)
+                     const char * const strings[], Error **errp)
 {
     Error *local_err = NULL;
     int64_t value = 0;
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index de77740..bf25249 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -115,7 +115,7 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
 }

 static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
-                                   const char *kind, size_t size, Error **errp)
+                                   size_t size, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index af600a9..db5e618 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -111,8 +111,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
 }

 static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
-                                    const char *kind, size_t unused,
-                                    Error **errp)
+                                    size_t unused, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QDict *dict = qdict_new();
diff --git a/qom/object.c b/qom/object.c
index 55b3955..e5b0566 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1244,7 +1244,7 @@ int object_property_get_enum(Object *obj, const char *name,
     siv = string_input_visitor_new(str);
     string_output_visitor_cleanup(sov);
     visit_type_enum(string_input_get_visitor(siv), name, &ret,
-                    enumprop->strings, NULL, errp);
+                    enumprop->strings, errp);

     g_free(str);
     string_input_visitor_cleanup(siv);
@@ -1915,7 +1915,7 @@ static void property_get_enum(Object *obj, Visitor *v, const char *name,
         return;
     }

-    visit_type_enum(v, name, &value, prop->strings, NULL, errp);
+    visit_type_enum(v, name, &value, prop->strings, errp);
 }

 static void property_set_enum(Object *obj, Visitor *v, const char *name,
@@ -1925,7 +1925,7 @@ static void property_set_enum(Object *obj, Visitor *v, const char *name,
     int value;
     Error *err = NULL;

-    visit_type_enum(v, name, &value, prop->strings, NULL, &err);
+    visit_type_enum(v, name, &value, prop->strings, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -2006,7 +2006,7 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name,
         goto out;
     }

-    visit_start_struct(v, name, NULL, "struct tm", 0, &err);
+    visit_start_struct(v, name, NULL, 0, &err);
     if (err) {
         goto out;
     }
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index fd03e38..a1dca29 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -63,7 +63,7 @@ def gen_event_send(name, arg_type):
     qov = qmp_output_visitor_new();
     v = qmp_output_get_visitor(qov);

-    visit_start_struct(v, "%(name)s", NULL, NULL, 0, &err);
+    visit_start_struct(v, "%(name)s", NULL, 0, &err);
 ''',
                      name=name)
         ret += gen_err_check()
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index ca28b08..8a741b6 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -122,7 +122,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
 {
     Error *err = NULL;

-    visit_start_struct(v, name, (void **)obj, "%(name)s", sizeof(%(c_name)s), &err);
+    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
@@ -138,7 +138,7 @@ out:
     error_propagate(errp, err);
 }
 ''',
-                 name=name, c_name=c_name(name))
+                 c_name=c_name(name))

     return ret

@@ -183,11 +183,11 @@ def gen_visit_enum(name):
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
 {
     int tmp = *obj;
-    visit_type_enum(v, name, &tmp, %(c_name)s_lookup, "%(name)s", errp);
+    visit_type_enum(v, name, &tmp, %(c_name)s_lookup, errp);
     *obj = tmp;
 }
 ''',
-                 c_name=c_name(name), name=name)
+                 c_name=c_name(name))


 def gen_visit_alternate(name, variants):
@@ -259,7 +259,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
 {
     Error *err = NULL;

-    visit_start_struct(v, name, (void **)obj, "%(name)s", sizeof(%(c_name)s), &err);
+    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
@@ -267,7 +267,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         goto out_obj;
     }
 ''',
-                 c_name=c_name(name), name=name)
+                 c_name=c_name(name))

     if base:
         ret += mcgen('''
diff --git a/vl.c b/vl.c
index 49ec0de..22062b5 100644
--- a/vl.c
+++ b/vl.c
@@ -2834,7 +2834,7 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     pdict = qemu_opts_to_qdict(opts, NULL);
     v = opts_get_visitor(ov);

-    visit_start_struct(v, NULL, NULL, NULL, 0, &err);
+    visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
     }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (16 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-20 19:03   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
                   ` (19 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, Alexander Graf, open list:sPAPR,
	marcandre.lureau, David Gibson

No backend was setting an error when ending the visit of a list
or implicit struct.  Make the callers a bit easier to follow by
making this a part of the contract, and removing the errp
argument - callers can then unconditionally end an object as
part of cleanup without having to think about whether a second
error is dominated by a first, because there is no second error.

The only addition of &error_abort in this patch, in the function
qmp_input_end_list(), will never trigger unless a programming
bug creates a push(struct)/pop(list) or push(list)/pop(struct)
mismatch.

A later patch will then tackle the larger task of splitting
visit_end_struct(), which can indeed set an error (and that
cleanup will also have the side-effect of removing the use of
error_abort added here).

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: enhance commit message
v8: no change
v7: place earlier in series, rebase to earlier changes
v6: new patch, split from RFC on v5 7/46
---
 hw/ppc/spapr_drc.c           |  6 +-----
 include/qapi/visitor-impl.h  |  6 ++++--
 include/qapi/visitor.h       |  5 +++--
 qapi/opts-visitor.c          |  2 +-
 qapi/qapi-dealloc-visitor.c  |  4 ++--
 qapi/qapi-visit-core.c       |  8 ++++----
 qapi/qmp-input-visitor.c     |  9 ++-------
 qapi/qmp-output-visitor.c    |  2 +-
 qapi/string-input-visitor.c  |  2 +-
 qapi/string-output-visitor.c |  2 +-
 scripts/qapi-visit.py        | 10 +++-------
 11 files changed, 23 insertions(+), 33 deletions(-)

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 78d71e2..ffc2cd9 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -314,11 +314,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
                     return;
                 }
             }
-            visit_end_list(v, &err);
-            if (err) {
-                error_propagate(errp, err);
-                return;
-            }
+            visit_end_list(v);
             break;
         }
         default:
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 6abfda7..7f512cf 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -24,11 +24,13 @@ struct Visitor

     void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
                                   Error **errp);
-    void (*end_implicit_struct)(Visitor *v, Error **errp);
+    /* May be NULL */
+    void (*end_implicit_struct)(Visitor *v);

     void (*start_list)(Visitor *v, const char *name, Error **errp);
     GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
-    void (*end_list)(Visitor *v, Error **errp);
+    /* Must be set */
+    void (*end_list)(Visitor *v);

     void (*type_enum)(Visitor *v, const char *name, int *obj,
                       const char *const strings[], Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 4abc180..10390d2 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -32,10 +32,11 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
 void visit_end_struct(Visitor *v, Error **errp);
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
-void visit_end_implicit_struct(Visitor *v, Error **errp);
+void visit_end_implicit_struct(Visitor *v);
+
 void visit_start_list(Visitor *v, const char *name, Error **errp);
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
-void visit_end_list(Visitor *v, Error **errp);
+void visit_end_list(Visitor *v);

 /**
  * Check if an optional member @name of an object needs visiting.
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 6d4a91e..62ffdd4 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -269,7 +269,7 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)


 static void
-opts_end_list(Visitor *v, Error **errp)
+opts_end_list(Visitor *v)
 {
     OptsVisitor *ov = to_ov(v);

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 49e7cf0..560feb3 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -83,7 +83,7 @@ static void qapi_dealloc_start_implicit_struct(Visitor *v,
     qapi_dealloc_push(qov, obj);
 }

-static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp)
+static void qapi_dealloc_end_implicit_struct(Visitor *v)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     void **obj = qapi_dealloc_pop(qov);
@@ -119,7 +119,7 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
     return NULL;
 }

-static void qapi_dealloc_end_list(Visitor *v, Error **errp)
+static void qapi_dealloc_end_list(Visitor *v)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     void *obj = qapi_dealloc_pop(qov);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index b0452cf..2d3743b 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -37,10 +37,10 @@ void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
     }
 }

-void visit_end_implicit_struct(Visitor *v, Error **errp)
+void visit_end_implicit_struct(Visitor *v)
 {
     if (v->end_implicit_struct) {
-        v->end_implicit_struct(v, errp);
+        v->end_implicit_struct(v);
     }
 }

@@ -54,9 +54,9 @@ GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
     return v->next_list(v, list, errp);
 }

-void visit_end_list(Visitor *v, Error **errp)
+void visit_end_list(Visitor *v)
 {
-    v->end_list(v, errp);
+    v->end_list(v);
 }

 bool visit_start_union(Visitor *v, bool data_present, Error **errp)
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index bf25249..597652c 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -153,10 +153,6 @@ static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
     }
 }

-static void qmp_input_end_implicit_struct(Visitor *v, Error **errp)
-{
-}
-
 static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -201,11 +197,11 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
     return entry;
 }

-static void qmp_input_end_list(Visitor *v, Error **errp)
+static void qmp_input_end_list(Visitor *v)
 {
     QmpInputVisitor *qiv = to_qiv(v);

-    qmp_input_pop(qiv, errp);
+    qmp_input_pop(qiv, &error_abort);
 }

 static void qmp_input_get_next_type(Visitor *v, const char *name, QType *type,
@@ -352,7 +348,6 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.start_struct = qmp_input_start_struct;
     v->visitor.end_struct = qmp_input_end_struct;
     v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
-    v->visitor.end_implicit_struct = qmp_input_end_implicit_struct;
     v->visitor.start_list = qmp_input_start_list;
     v->visitor.next_list = qmp_input_next_list;
     v->visitor.end_list = qmp_input_end_list;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index db5e618..d367148 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -151,7 +151,7 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
     return list ? list->next : NULL;
 }

-static void qmp_output_end_list(Visitor *v, Error **errp)
+static void qmp_output_end_list(Visitor *v)
 {
     QmpOutputVisitor *qov = to_qov(v);
     qmp_output_pop(qov);
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 5347b61..610c233 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -178,7 +178,7 @@ next_list(Visitor *v, GenericList **list, Error **errp)
 }

 static void
-end_list(Visitor *v, Error **errp)
+end_list(Visitor *v)
 {
     StringInputVisitor *siv = to_siv(v);
     siv->head = true;
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 74de6b6..fd917a4 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -303,7 +303,7 @@ next_list(Visitor *v, GenericList **list, Error **errp)
 }

 static void
-end_list(Visitor *v, Error **errp)
+end_list(Visitor *v)
 {
     StringOutputVisitor *sov = to_sov(v);

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 8a741b6..573bb81 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -62,7 +62,7 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
     visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
     if (!err) {
         visit_type_%(c_type)s_fields(v, obj, errp);
-        visit_end_implicit_struct(v, &err);
+        visit_end_implicit_struct(v);
     }
     error_propagate(errp, err);
 }
@@ -167,9 +167,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
     }

-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_list(v, &err);
+    visit_end_list(v);
 out:
     error_propagate(errp, err);
 }
@@ -230,9 +228,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
                    "%(name)s");
     }
 out_obj:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_implicit_struct(v, &err);
+    visit_end_implicit_struct(v);
 out:
     error_propagate(errp, err);
 }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (17 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-21 10:27   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root Eric Blake
                   ` (18 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: Michael Roth, marcandre.lureau, armbru, qemu-stable

Commit 6c2f9a15 ensured that we would not return NULL when the
caller used an output visitor but had nothing to visit. But
in doing so, it added a FIXME about a reference count leak
that could abort qemu in the (unlikely) case of SIZE_MAX such
visits (more plausible on 32-bit).  (Although that commit
suggested we might fix it in time for 2.5, we ran out of time;
fortunately, it is unlikely enough to bite that it was not
worth worrying about during the 2.5 release.)

This fixes things by documenting the internal contracts, and
explaining why the internal function can return NULL and only
the public facing interface needs to worry about qnull(),
thus avoiding over-referencing the qnull_ global object.

It does not, however, fix the stupidity of the stack mixing
up two separate pieces of information; add a FIXME to explain
that issue, which will be fixed shortly in a future patch.

Signed-off-by: Eric Blake <eblake@redhat.com>
Cc: qemu-stable@nongnu.org
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: enhance commit message
v8: rebase to earlier changes
v7: cc qemu-stable, tweak some asserts, drop stale comment, add more
comments
v6: no change
---
 qapi/qmp-output-visitor.c       | 39 ++++++++++++++++++++++++++++++++-------
 tests/test-qmp-output-visitor.c |  2 ++
 2 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index d367148..316f4e4 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -29,6 +29,15 @@ typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
 struct QmpOutputVisitor
 {
     Visitor visitor;
+    /* FIXME: we are abusing stack to hold two separate pieces of
+     * information: the current root object in slot 0, and the stack
+     * of N objects still being built in slots 1 through N (for N+1
+     * slots in use).  Worse, our behavior is inconsistent:
+     * qmp_output_add_obj() visiting two top-level scalars in a row
+     * discards the first in favor of the second, but visiting two
+     * top-level objects in a row tries to append the second object
+     * into the first (since the first object was placed in the stack
+     * in both slot 0 and 1, but only popped from slot 1).  */
     QStack stack;
 };

@@ -41,10 +50,12 @@ static QmpOutputVisitor *to_qov(Visitor *v)
     return container_of(v, QmpOutputVisitor, visitor);
 }

+/* Push @value onto the stack of current QObjects being built */
 static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
 {
     QStackEntry *e = g_malloc0(sizeof(*e));

+    assert(value);
     e->value = value;
     if (qobject_type(e->value) == QTYPE_QLIST) {
         e->is_list_head = true;
@@ -52,44 +63,51 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
     QTAILQ_INSERT_HEAD(&qov->stack, e, node);
 }

+/* Grab and remove the most recent QObject from the stack */
 static QObject *qmp_output_pop(QmpOutputVisitor *qov)
 {
     QStackEntry *e = QTAILQ_FIRST(&qov->stack);
     QObject *value;
+
+    assert(e);
     QTAILQ_REMOVE(&qov->stack, e, node);
     value = e->value;
     g_free(e);
     return value;
 }

+/* Grab the root QObject, if any, in preparation to empty the stack */
 static QObject *qmp_output_first(QmpOutputVisitor *qov)
 {
     QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);

-    /*
-     * FIXME Wrong, because qmp_output_get_qobject() will increment
-     * the refcnt *again*.  We need to think through how visitors
-     * handle null.
-     */
     if (!e) {
-        return qnull();
+        /* No root */
+        return NULL;
     }
-
+    assert(e->value);
     return e->value;
 }

+/* Grab the most recent QObject from the stack, which must exist */
 static QObject *qmp_output_last(QmpOutputVisitor *qov)
 {
     QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+
+    assert(e && e->value);
     return e->value;
 }

+/* Add @value to the current QObject being built.
+ * If the stack is visiting a dictionary or list, @value is now owned
+ * by that container. Otherwise, @value is now the root.  */
 static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
                                QObject *value)
 {
     QObject *cur;

     if (QTAILQ_EMPTY(&qov->stack)) {
+        /* Stack was empty, track this object as root */
         qmp_output_push_obj(qov, value);
         return;
     }
@@ -98,13 +116,17 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,

     switch (qobject_type(cur)) {
     case QTYPE_QDICT:
+        assert(name);
         qdict_put_obj(qobject_to_qdict(cur), name, value);
         break;
     case QTYPE_QLIST:
         qlist_append_obj(qobject_to_qlist(cur), value);
         break;
     default:
+        /* The previous root was a scalar, replace it with a new root */
+        /* FIXME this is abusing the stack; see comment above */
         qobject_decref(qmp_output_pop(qov));
+        assert(QTAILQ_EMPTY(&qov->stack));
         qmp_output_push_obj(qov, value);
         break;
     }
@@ -205,11 +227,14 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
     qmp_output_add_obj(qov, name, *obj);
 }

+/* Finish building, and return the root object. Will not be NULL. */
 QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 {
     QObject *obj = qmp_output_first(qov);
     if (obj) {
         qobject_incref(obj);
+    } else {
+        obj = qnull();
     }
     return obj;
 }
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 4df94bc..26dc752 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -461,6 +461,8 @@ static void test_visitor_out_empty(TestOutputVisitorData *data,

     arg = qmp_output_get_qobject(data->qov);
     g_assert(qobject_type(arg) == QTYPE_QNULL);
+    /* Check that qnull reference counting is sane */
+    g_assert(arg->refcnt == 2);
     qobject_decref(arg);
 }

-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (18 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-21 13:58   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions Eric Blake
                   ` (17 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

The previous commit documented an inconsistency in how we are
using the stack of qmp-output-visitor.  Normally, pushing a
single top-level object puts the object on the stack twice:
once as the root, and once as the current container being
appended to; but popping that struct only pops once.  However,
qmp_ouput_add() was trying to either set up the added object
as the new root (works if you parse two top-level scalars in a
row: the second replaces the first as the root) or as a member
of the current container (works as long as you have an open
container on the stack; but if you have popped the first
top-level container, it then resolves to the root and still
tries to add into that existing container).

Fix the stupidity by not tracking two separate things in the
stack.  Drop the now-useless qmp_output_first() while at it.

Saved for a later patch: we still are rather sloppy in that
qmp_output_get_object() can be called in the middle of a parse,
rather than requiring that a visit is complete.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to earlier changes
v7: retitle; rebase to earlier changes, drop qmp_output_first()
v6: no change
---
 qapi/qmp-output-visitor.c | 79 ++++++++++++++++-------------------------------
 1 file changed, 26 insertions(+), 53 deletions(-)

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 316f4e4..df22999 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -29,16 +29,8 @@ typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
 struct QmpOutputVisitor
 {
     Visitor visitor;
-    /* FIXME: we are abusing stack to hold two separate pieces of
-     * information: the current root object in slot 0, and the stack
-     * of N objects still being built in slots 1 through N (for N+1
-     * slots in use).  Worse, our behavior is inconsistent:
-     * qmp_output_add_obj() visiting two top-level scalars in a row
-     * discards the first in favor of the second, but visiting two
-     * top-level objects in a row tries to append the second object
-     * into the first (since the first object was placed in the stack
-     * in both slot 0 and 1, but only popped from slot 1).  */
-    QStack stack;
+    QStack stack; /* Stack of containers that haven't yet been finished */
+    QObject *root; /* Root of the output visit */
 };

 #define qmp_output_add(qov, name, value) \
@@ -55,6 +47,7 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
 {
     QStackEntry *e = g_malloc0(sizeof(*e));

+    assert(qov->root);
     assert(value);
     e->value = value;
     if (qobject_type(e->value) == QTYPE_QLIST) {
@@ -76,26 +69,12 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
     return value;
 }

-/* Grab the root QObject, if any, in preparation to empty the stack */
-static QObject *qmp_output_first(QmpOutputVisitor *qov)
-{
-    QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
-
-    if (!e) {
-        /* No root */
-        return NULL;
-    }
-    assert(e->value);
-    return e->value;
-}
-
-/* Grab the most recent QObject from the stack, which must exist */
+/* Grab the most recent QObject from the stack, if any */
 static QObject *qmp_output_last(QmpOutputVisitor *qov)
 {
     QStackEntry *e = QTAILQ_FIRST(&qov->stack);

-    assert(e && e->value);
-    return e->value;
+    return e ? e->value : NULL;
 }

 /* Add @value to the current QObject being built.
@@ -106,29 +85,25 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
 {
     QObject *cur;

-    if (QTAILQ_EMPTY(&qov->stack)) {
-        /* Stack was empty, track this object as root */
-        qmp_output_push_obj(qov, value);
-        return;
-    }
-
     cur = qmp_output_last(qov);

-    switch (qobject_type(cur)) {
-    case QTYPE_QDICT:
-        assert(name);
-        qdict_put_obj(qobject_to_qdict(cur), name, value);
-        break;
-    case QTYPE_QLIST:
-        qlist_append_obj(qobject_to_qlist(cur), value);
-        break;
-    default:
-        /* The previous root was a scalar, replace it with a new root */
-        /* FIXME this is abusing the stack; see comment above */
-        qobject_decref(qmp_output_pop(qov));
-        assert(QTAILQ_EMPTY(&qov->stack));
-        qmp_output_push_obj(qov, value);
-        break;
+    if (!cur) {
+        /* FIXME we should require the user to reset the visitor, rather
+         * than throwing away the previous root */
+        qobject_decref(qov->root);
+        qov->root = value;
+    } else {
+        switch (qobject_type(cur)) {
+        case QTYPE_QDICT:
+            assert(name);
+            qdict_put_obj(qobject_to_qdict(cur), name, value);
+            break;
+        case QTYPE_QLIST:
+            qlist_append_obj(qobject_to_qlist(cur), value);
+            break;
+        default:
+            g_assert_not_reached();
+        }
     }
 }

@@ -230,7 +205,9 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
 /* Finish building, and return the root object. Will not be NULL. */
 QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 {
-    QObject *obj = qmp_output_first(qov);
+    /* FIXME: we should require that a visit occurred, and that it is
+     * complete (no starts without a matching end) */
+    QObject *obj = qov->root;
     if (obj) {
         qobject_incref(obj);
     } else {
@@ -248,16 +225,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
 {
     QStackEntry *e, *tmp;

-    /* The bottom QStackEntry, if any, owns the root QObject. See the
-     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
-    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
-
     QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
         QTAILQ_REMOVE(&v->stack, e, node);
         g_free(e);
     }

-    qobject_decref(root);
+    qobject_decref(v->root);
     g_free(v);
 }

-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (19 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-21 20:08   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor Eric Blake
                   ` (16 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

The visitor interface for mapping between QObject/QemuOpts/string
and qapi has formerly been documented only by reading source code,
making it difficult to propose changes to either scripts/qapi*.py
or to clients without knowing whether those changes would be safe.
This adds documentation, including mentioning when parameters can
be NULL, and where there are still some interface warts that would
be nice to remove.  In particular, I have plans to remove
visit_start_union() in a future patch.

Add some asserts to strengthen the claims of the documentation; some
of these were only made possible by recent cleanup commits.  These
were made easier with the addition of a new visit_is_output()
helper (since all 2 output visitors of our 6 overall visitors use
the same .type_enum() callback).

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to 'name' motion
v7: retitle; more wording changes, add asserts to enforce the
wording, place later in series to rebase on fixes that would
otherwise trip the new assertions
v6: mention that input visitors blindly assign over *obj; wording
improvements
---
 include/qapi/visitor-impl.h |  31 ++++++-
 include/qapi/visitor.h      | 196 ++++++++++++++++++++++++++++++++++++++++++++
 qapi/qapi-visit-core.c      |  39 ++++++++-
 3 files changed, 262 insertions(+), 4 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 7f512cf..aab46bc 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -15,23 +15,37 @@
 #include "qapi/error.h"
 #include "qapi/visitor.h"

+/* This file describes the callback interface for implementing a
+ * QObject visitor.  For the client interface, see visitor.h.  When
+ * implementing the callbacks, it is easiest to declare a struct with
+ * 'Visitor visitor;' as the first member.  Semantics for the
+ * callbacks are generally similar to the counterpart public
+ * interface.  */
+
 struct Visitor
 {
-    /* Must be set */
+    /* Must be provided to visit structs (the string visitors do not
+     * currently visit structs). */
     void (*start_struct)(Visitor *v, const char *name, void **obj,
                          size_t size, Error **errp);
+    /* Must be provided if start_struct is present. */
     void (*end_struct)(Visitor *v, Error **errp);

+    /* May be NULL; most useful for input visitors. */
     void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
                                   Error **errp);
     /* May be NULL */
     void (*end_implicit_struct)(Visitor *v);

+    /* Must be set */
     void (*start_list)(Visitor *v, const char *name, Error **errp);
+    /* Must be set */
     GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
     /* Must be set */
     void (*end_list)(Visitor *v);

+    /* Must be set, although the helpers input_type_enum() and
+     * output_type_enum() should be used if appropriate.  */
     void (*type_enum)(Visitor *v, const char *name, int *obj,
                       const char *const strings[], Error **errp);
     /* May be NULL; only needed for input visitors. */
@@ -47,23 +61,38 @@ struct Visitor
     /* Optional; fallback is type_uint64().  */
     void (*type_size)(Visitor *v, const char *name, uint64_t *obj,
                       Error **errp);
+
     /* Must be set. */
     void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp);
+    /* Must be set */
     void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp);
+
+    /* Must be provided to visit numbers (the opts visitor does not
+     * currently visit non-integers). */
     void (*type_number)(Visitor *v, const char *name, double *obj,
                         Error **errp);
+    /* Must be provided to visit arbitrary QTypes (the opts and string
+     * visitors do not currently visit arbitrary types).  */
     void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);

     /* May be NULL; most useful for input visitors. */
     void (*optional)(Visitor *v, const char *name, bool *present);

+    /* FIXME - needs to be removed */
     bool (*start_union)(Visitor *v, bool data_present, Error **errp);
+    /* FIXME - needs to be removed */
     void (*end_union)(Visitor *v, bool data_present, Error **errp);
 };

+/**
+ * A generic visitor.type_enum suitable for input visitors.
+ */
 void input_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp);
+/**
+ * A generic visitor.type_enum suitable for output visitors.
+ */
 void output_type_enum(Visitor *v, const char *name, int *obj,
                       const char *const strings[], Error **errp);

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 10390d2..5349a64 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -18,6 +18,20 @@
 #include "qapi/error.h"
 #include <stdlib.h>

+/* This file describes the client view for visiting a map between
+ * generated QAPI C structs and another representation (command line
+ * options, strings, or QObjects).  An input visitor converts from
+ * some other form into QAPI representation; an output visitor
+ * converts from QAPI back into another form.  In the descriptions
+ * below, an object or dictionary refers to a JSON '{}', and a list
+ * refers to a JSON '[]'.  These functions seldom need to be called
+ * directly, but are instead used by code generated by
+ * scripts/qapi-visit.py.  For the visitor callback contracts, see
+ * visitor-impl.h.
+ */
+
+/* This struct is layout-compatible with all other *List structs
+ * created by the qapi generator. */
 typedef struct GenericList
 {
     union {
@@ -27,15 +41,101 @@ typedef struct GenericList
     struct GenericList *next;
 } GenericList;

+/**
+ * Prepare to visit an object tied to key @name.
+ * @name will be NULL if this is visited as part of a list.  The
+ * caller then makes a series of visit calls for each key expected in
+ * the object, where those visits set their respective obj parameter
+ * to the address of a member of the qapi struct, and follows
+ * everything by a call to visit_end_struct() to clean up resources.
+ *
+ * @obj can be NULL (in which case @size should also be 0) to indicate
+ * that there is no qapi C struct, and that the upcoming visit calls
+ * are parsing input to or creating output from some other
+ * representation.
+ *
+ * If @obj is not NULL, then input visitors malloc a qapi struct of
+ * @size bytes into *@obj on success, and output visitors expect *@obj
+ * to be a fully-populated qapi struct.
+ *
+ * Set *@errp on failure; for example, if the input stream does not
+ * have a member @name or if the member is not an object.
+ *
+ * FIXME: For input visitors, *@obj can be assigned here even if later
+ * visits will fail; this can lead to memory leaks if clients aren't
+ * careful.
+ */
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp);
+/**
+ * Complete a struct visit started earlier.
+ * Must be called after any successful use of visit_start_struct(),
+ * even if intermediate processing was skipped due to errors, to allow
+ * the backend to release any resources.
+ */
 void visit_end_struct(Visitor *v, Error **errp);
+
+/**
+ * Prepare to visit an implicit struct.
+ * Similar to visit_start_struct(), except that an implicit struct
+ * represents a subset of keys that are present at the same nesting level
+ * of a common object but as a separate qapi C struct, rather than a new
+ * object at a deeper nesting level.
+ *
+ * @obj must not be NULL, since this function is only called when
+ * visiting with qapi structs.
+ *
+ * FIXME: For input visitors, *@obj can be assigned here even if later
+ * visits will fail; this can lead to memory leaks if clients aren't
+ * careful.
+ */
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
+/**
+ * Complete an implicit struct visit started earlier.
+ * Must be called after any successful use of visit_start_implicit_struct(),
+ * even if intermediate processing was skipped due to errors, to allow
+ * the backend to release any resources.  Unlike visit_end_struct(), this
+ * does not need to check for errors (detection of unused keys is only
+ * possible for the overall struct, not a subset).
+ */
 void visit_end_implicit_struct(Visitor *v);

+/**
+ * Prepare to visit a list tied to an object key @name.
+ * @name will be NULL if this is visited as part of another list.
+ * After calling this, the elements must be collected until
+ * visit_next_list() returns NULL, then visit_end_list() must be
+ * used to complete the visit.
+ */
 void visit_start_list(Visitor *v, const char *name, Error **errp);
+/**
+ * Iterate over a GenericList during a list visit.
+ * @list must not be NULL; on the first call, @list contains the
+ * address of the list head, and on subsequent calls *@list must be
+ * the previously returned value.  Must be called in a loop until a
+ * NULL return or error occurs; for each non-NULL return, the caller
+ * must then call the appropriate visit_type_*() for the element type
+ * of the list, with that function's name parameter set to NULL.
+ *
+ * Note that for some visitors (qapi-dealloc and qmp-output), when a
+ * qapi GenericList linked list is not being used (comparable to when
+ * a NULL obj is used for visit_start_struct()), it is acceptable to
+ * bypass the use of visit_next_list() and just directly call the
+ * appropriate visit_type_*() for each element in between the
+ * visit_start_list() and visit_end_list() calls.
+ *
+ * FIXME: For input visitors, *@list can be assigned here even if
+ * later visits will fail; this can lead to memory leaks if clients
+ * aren't careful.
+ */
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
+/**
+ * Complete the list started earlier.
+ * Must be called after any successful use of visit_start_list(),
+ * even if intermediate processing was skipped due to errors, to allow
+ * the backend to release any resources.
+ */
 void visit_end_list(Visitor *v);

 /**
@@ -54,32 +154,128 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
  */
 void visit_get_next_type(Visitor *v, const char *name, QType *type,
                          bool promote_int, Error **errp);
+
+/**
+ * Visit an enum value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * For input visitors, parse a string and set *@obj to the numeric
+ * value of the enum type using @strings as the mapping, leaving @obj
+ * unchanged on error; for output visitors, reverse the mapping and
+ * visit the output string determined by *@obj.
+ */
 void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp);
+
+/**
+ * Visit an integer value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * For input visitors, set *@obj to the parsed value; for other visitors,
+ * leave *@obj unchanged.
+ */
 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
+/**
+ * Visit a uint8_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to uint8_t range.
+ */
 void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
                       Error **errp);
+/**
+ * Visit a uint16_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to uint16_t range.
+ */
 void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj,
                        Error **errp);
+/**
+ * Visit a uint32_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to uint32_t range.
+ */
 void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
                        Error **errp);
+/**
+ * Visit a uint64_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to uint64_t range
+ * (that is, ensures it is unsigned).
+ */
 void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                        Error **errp);
+/**
+ * Visit an int8_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to int8_t range.
+ */
 void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp);
+/**
+ * Visit an int16_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to int16_t range.
+ */
 void visit_type_int16(Visitor *v, const char *name, int16_t *obj,
                       Error **errp);
+/**
+ * Visit an int32_t value tied to @name in the current object visit.
+ * Like visit_type_int(), except clamps the value to int32_t range.
+ */
 void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
                       Error **errp);
+/**
+ * Visit an int64_t value tied to @name in the current object visit.
+ * Identical to visit_type_int().
+ */
 void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
                       Error **errp);
+/**
+ * Visit a uint64_t value tied to @name in the current object visit.
+ * Like visit_type_uint64(), except that some visitors may choose to
+ * recognize additional suffixes for easily scaling input values.
+ */
 void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
                      Error **errp);
+
+/**
+ * Visit a boolean value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * Input visitors set *@obj to the value; other visitors will leave
+ * *@obj unchanged.
+ */
 void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
+
+/**
+ * Visit a string value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * @obj must be non-NULL.  Input visitors set *@obj to the parsed
+ * string (never NULL); while output visitors leave *@obj unchanged,
+ * except that a NULL *@obj will be treated the same as "".
+ *
+ * FIXME: Unfortunately not const-correct for output visitors.
+ * FIXME: Callers that try to output NULL *obj should not be allowed.
+ */
 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
+
+/**
+ * Visit a number value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * Input visitors set *@obj to the value; other visitors will leave
+ * *@obj unchanged.
+ */
 void visit_type_number(Visitor *v, const char *name, double *obj,
                        Error **errp);
+
+/**
+ * Visit an arbitrary qtype value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * Input visitors set *@obj to the value; other visitors will leave
+ * *@obj (which must not be NULL) unchanged.
+ */
 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
+
+/**
+ * Mark the start of visiting the branches of a union. Return true if
+ * @data_present.
+ * FIXME: Should not be needed
+ */
 bool visit_start_union(Visitor *v, bool data_present, Error **errp);
+/**
+ * Mark the end of union branches, after visit_start_union().
+ * FIXME: Should not be needed
+ */
 void visit_end_union(Visitor *v, bool data_present, Error **errp);

 #endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 2d3743b..1612d0d 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -18,9 +18,21 @@
 #include "qapi/visitor.h"
 #include "qapi/visitor-impl.h"

+/* Determine if this is an output visitor.
+ * Useful for making some tighter assertions that hold for output
+ * visitors, but not for input or dealloc visitors. */
+static bool visit_is_output(Visitor *v)
+{
+    return v->type_enum == output_type_enum;
+}
+
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp)
 {
+    assert(obj ? size : !size);
+    if (obj && visit_is_output(v)) {
+        assert(*obj);
+    }
     v->start_struct(v, name, obj, size, errp);
 }

@@ -32,6 +44,10 @@ void visit_end_struct(Visitor *v, Error **errp)
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp)
 {
+    assert(obj && size);
+    if (visit_is_output(v)) {
+        assert(*obj);
+    }
     if (v->start_implicit_struct) {
         v->start_implicit_struct(v, obj, size, errp);
     }
@@ -51,6 +67,7 @@ void visit_start_list(Visitor *v, const char *name, Error **errp)

 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
 {
+    assert(list);
     return v->next_list(v, list, errp);
 }

@@ -85,6 +102,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
 void visit_get_next_type(Visitor *v, const char *name, QType *type,
                          bool promote_int, Error **errp)
 {
+    assert(type);
     if (v->get_next_type) {
         v->get_next_type(v, name, type, promote_int, errp);
     }
@@ -93,11 +111,13 @@ void visit_get_next_type(Visitor *v, const char *name, QType *type,
 void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp)
 {
+    assert(obj && strings);
     v->type_enum(v, name, obj, strings, errp);
 }

 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
 {
+    assert(obj);
     v->type_int64(v, name, obj, errp);
 }

@@ -145,6 +165,7 @@ void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
 void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                        Error **errp)
 {
+    assert(obj);
     v->type_uint64(v, name, obj, errp);
 }

@@ -192,12 +213,14 @@ void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
 void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
                       Error **errp)
 {
+    assert(obj);
     v->type_int64(v, name, obj, errp);
 }

 void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
                      Error **errp)
 {
+    assert(obj);
     if (v->type_size) {
         v->type_size(v, name, obj, errp);
     } else {
@@ -207,22 +230,35 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj,

 void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
 {
+    assert(obj);
     v->type_bool(v, name, obj, errp);
 }

 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
+    assert(obj);
+    /* TODO: Fix callers to not pass NULL when they mean "", so that we
+     * can enable:
+    if (visit_is_output(v)) {
+        assert(*obj);
+    }
+     */
     v->type_str(v, name, obj, errp);
 }

 void visit_type_number(Visitor *v, const char *name, double *obj,
                        Error **errp)
 {
+    assert(obj);
     v->type_number(v, name, obj, errp);
 }

 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 {
+    assert(obj);
+    if (visit_is_output(v)) {
+        assert(*obj);
+    }
     v->type_any(v, name, obj, errp);
 }

@@ -233,7 +269,6 @@ void output_type_enum(Visitor *v, const char *name, int *obj,
     int value = *obj;
     char *enum_str;

-    assert(strings);
     while (strings[i++] != NULL);
     if (value < 0 || value >= i - 1) {
         error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null");
@@ -251,8 +286,6 @@ void input_type_enum(Visitor *v, const char *name, int *obj,
     int64_t value = 0;
     char *enum_str;

-    assert(strings);
-
     visit_type_str(v, name, &enum_str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (20 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-22 17:00   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit Eric Blake
                   ` (15 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Right now, qmp-output-visitor happens to produce a QNull result
if nothing is actually visited between the creation of the visitor
and the request for the resulting QObject.  A stronger protocol
would require that a QMP output visit MUST visit something.  But
to still be able to produce a JSON 'null' output, we need a new
visitor function that states our intentions.

This patch introduces the new visit_type_null() interface, and
later patches will then wire it up into the qmp visitors.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to 'name' motion
v7: new patch, based on discussion about spapr_drc.c
---
 include/qapi/visitor-impl.h | 3 +++
 include/qapi/visitor.h      | 8 ++++++++
 qapi/qapi-dealloc-visitor.c | 5 +++++
 qapi/qapi-visit-core.c      | 5 +++++
 4 files changed, 21 insertions(+)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index aab46bc..8705136 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -75,6 +75,9 @@ struct Visitor
      * visitors do not currently visit arbitrary types).  */
     void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);
+    /* Must be provided to visit explicit null values (right now, only the
+     * dealloc visitor supports this).  */
+    void (*type_null)(Visitor *v, const char *name, Error **errp);

     /* May be NULL; most useful for input visitors. */
     void (*optional)(Visitor *v, const char *name, bool *present);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 5349a64..6e49b51 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -267,6 +267,14 @@ void visit_type_number(Visitor *v, const char *name, double *obj,
 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);

 /**
+ * Visit a JSON null value tied to @name in the current object visit.
+ * @name will be NULL if this is visited as part of a list.
+ * No obj parameter is needed; rather, this is a witness that an
+ * explicit null value is expected rather than any other type.
+ */
+void visit_type_null(Visitor *v, const char *name, Error **errp);
+
+/**
  * Mark the start of visiting the branches of a union. Return true if
  * @data_present.
  * FIXME: Should not be needed
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 560feb3..ede1703 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -162,6 +162,10 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name,
     }
 }

+static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
+{
+}
+
 static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
                                    const char * const strings[], Error **errp)
 {
@@ -222,6 +226,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_str = qapi_dealloc_type_str;
     v->visitor.type_number = qapi_dealloc_type_number;
     v->visitor.type_any = qapi_dealloc_type_anything;
+    v->visitor.type_null = qapi_dealloc_type_null;
     v->visitor.start_union = qapi_dealloc_start_union;

     QTAILQ_INIT(&v->stack);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 1612d0d..399256b 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -262,6 +262,11 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
     v->type_any(v, name, obj, errp);
 }

+void visit_type_null(Visitor *v, const char *name, Error **errp)
+{
+    v->type_null(v, name, errp);
+}
+
 void output_type_enum(Visitor *v, const char *name, int *obj,
                       const char * const strings[], Error **errp)
 {
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (21 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-22 17:12   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules Eric Blake
                   ` (14 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Implement the new type_null() callback for the qmp input visitor.
While we don't yet have a use for this in qapi (the generator
will need some tweaks first), one usage is already envisioned:
when changing blockdev parameters, it would be nice to have a
difference between leaving a tuning parameter unchanged (omit
that parameter from the struct) and to explicitly reset the
parameter to its default without having to know what the default
value is (specify the parameter with an explicit null value,
which will require us to allow a qapi alternate that chooses
between the normal value and an explicit null).

At any rate, we can test this without the use of generated qapi
by manually using visit_start_struct()/visit_end_struct().

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

---
v9: new patch
---
 include/qapi/visitor-impl.h    |  2 +-
 qapi/qmp-input-visitor.c       | 14 ++++++++++++++
 tests/test-qmp-input-visitor.c | 26 ++++++++++++++++++++++++++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 8705136..95408a5 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -76,7 +76,7 @@ struct Visitor
     void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);
     /* Must be provided to visit explicit null values (right now, only the
-     * dealloc visitor supports this).  */
+     * dealloc and qmp-input visitors support this).  */
     void (*type_null)(Visitor *v, const char *name, Error **errp);

     /* May be NULL; most useful for input visitors. */
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 597652c..ad23bec 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -315,6 +315,19 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
     *obj = qobj;
 }

+static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
+{
+    QmpInputVisitor *qiv = to_qiv(v);
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+
+    if (qobject_type(qobj) == QTYPE_QNULL) {
+        return;
+    }
+
+    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+               "null");
+}
+
 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -358,6 +371,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
     v->visitor.type_any = qmp_input_type_any;
+    v->visitor.type_null = qmp_input_type_null;
     v->visitor.optional = qmp_input_optional;
     v->visitor.get_next_type = qmp_input_get_next_type;

diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index f6bd408..6489e4a 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -278,6 +278,30 @@ static void test_visitor_in_any(TestInputVisitorData *data,
     qobject_decref(res);
 }

+static void test_visitor_in_null(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    Visitor *v;
+    QObject *null;
+
+    v = visitor_input_test_init(data, "null");
+    visit_type_null(v, NULL, &error_abort);
+
+    v = visitor_input_test_init(data, "{ 'a': null }");
+    visit_start_struct(v, NULL, NULL, 0, &error_abort);
+    visit_type_null(v, "a", &error_abort);
+    visit_end_struct(v, &error_abort);
+
+    /* Check that qnull reference counting is sane:
+     * 1 for global use, 1 for our qnull() use, and 1 still owned by 'v'
+     * until it is torn down */
+    null = qnull();
+    g_assert(null->refcnt == 3);
+    visitor_input_teardown(data, NULL);
+    g_assert(null->refcnt == 2);
+    qobject_decref(null);
+}
+
 static void test_visitor_in_union_flat(TestInputVisitorData *data,
                                        const void *unused)
 {
@@ -792,6 +816,8 @@ int main(int argc, char **argv)
                            &in_visitor_data, test_visitor_in_list);
     input_visitor_test_add("/visitor/input/any",
                            &in_visitor_data, test_visitor_in_any);
+    input_visitor_test_add("/visitor/input/null",
+                           &in_visitor_data, test_visitor_in_null);
     input_visitor_test_add("/visitor/input/union-flat",
                            &in_visitor_data, test_visitor_in_union_flat);
     input_visitor_test_add("/visitor/input/alternate",
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (22 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-22 19:11   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
                   ` (13 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Add a new qmp_output_visitor_reset(), which must be called before
reusing an exising QmpOutputVisitor on a new root object.  Tighten
assertions to require that qmp_output_get_qobject() can only be
called after pairing a visit_end_* for every visit_start_* (rather
than allowing it to return a partially built object), that it must
not be called unless at least one visit_type_* or visit_start/
visit_end pair has occurred since creation/reset (the accidental
return of NULL fixed by commit ab8bf1d7 would have been much
easier to diagnose), and that it may only be called once per visit.

Meanwhile, nothing was using the return value of qmp_output_pop().
Also, adding a parameter will let us diagnose any programming bugs
due to mismatched push(struct)/pop(list) or push(list)/pop(struct).

To keep the semantics of test_visitor_out_empty, we now have to
explicitly request a top-level visit of a NULL object, by
implementing the just-added visitor type_null() callback.

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

---
v9: rebase to added patch, squash in more sanity checks, drop
Marc-Andre's R-b
v8: rename qmp_output_reset to qmp_output_visitor_reset
v7: new patch, based on discussion about spapr_drc.c
---
 include/qapi/qmp-output-visitor.h |  1 +
 include/qapi/visitor-impl.h       |  4 ++--
 qapi/qmp-output-visitor.c         | 50 +++++++++++++++++++++++----------------
 tests/test-qmp-output-visitor.c   |  2 ++
 4 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
index 2266770..5093f0d 100644
--- a/include/qapi/qmp-output-visitor.h
+++ b/include/qapi/qmp-output-visitor.h
@@ -21,6 +21,7 @@ typedef struct QmpOutputVisitor QmpOutputVisitor;

 QmpOutputVisitor *qmp_output_visitor_new(void);
 void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
+void qmp_output_visitor_reset(QmpOutputVisitor *v);

 QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
 Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 95408a5..913f1b0 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -75,8 +75,8 @@ struct Visitor
      * visitors do not currently visit arbitrary types).  */
     void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);
-    /* Must be provided to visit explicit null values (right now, only the
-     * dealloc and qmp-input visitors support this).  */
+    /* Must be provided to visit explicit null values (the opts and string
+     * visitors do not currently visit an explicit null).  */
     void (*type_null)(Visitor *v, const char *name, Error **errp);

     /* May be NULL; most useful for input visitors. */
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index df22999..2eb200d 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -1,6 +1,7 @@
 /*
  * Core Definitions for QAPI/QMP Command Registry
  *
+ * Copyright (C) 2015-2016 Red Hat, Inc.
  * Copyright IBM, Corp. 2011
  *
  * Authors:
@@ -56,17 +57,15 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
     QTAILQ_INSERT_HEAD(&qov->stack, e, node);
 }

-/* Grab and remove the most recent QObject from the stack */
-static QObject *qmp_output_pop(QmpOutputVisitor *qov)
+/* Remove the most recent QObject with given type from the stack */
+static void qmp_output_pop(QmpOutputVisitor *qov, QType type)
 {
     QStackEntry *e = QTAILQ_FIRST(&qov->stack);
-    QObject *value;

     assert(e);
     QTAILQ_REMOVE(&qov->stack, e, node);
-    value = e->value;
+    assert(qobject_type(e->value) == type);
     g_free(e);
-    return value;
 }

 /* Grab the most recent QObject from the stack, if any */
@@ -88,9 +87,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
     cur = qmp_output_last(qov);

     if (!cur) {
-        /* FIXME we should require the user to reset the visitor, rather
-         * than throwing away the previous root */
-        qobject_decref(qov->root);
+        /* Don't allow reuse of visitor on more than one root */
+        assert(!qov->root);
         qov->root = value;
     } else {
         switch (qobject_type(cur)) {
@@ -99,6 +97,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
             qdict_put_obj(qobject_to_qdict(cur), name, value);
             break;
         case QTYPE_QLIST:
+            assert(!name);
             qlist_append_obj(qobject_to_qlist(cur), value);
             break;
         default:
@@ -120,7 +119,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
 static void qmp_output_end_struct(Visitor *v, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    qmp_output_pop(qov);
+    qmp_output_pop(qov, QTYPE_QDICT);
 }

 static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
@@ -151,7 +150,7 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
 static void qmp_output_end_list(Visitor *v)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    qmp_output_pop(qov);
+    qmp_output_pop(qov, QTYPE_QLIST);
 }

 static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj,
@@ -202,18 +201,22 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
     qmp_output_add_obj(qov, name, *obj);
 }

+static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
+{
+    QmpOutputVisitor *qov = to_qov(v);
+    qmp_output_add_obj(qov, name, qnull());
+}
+
 /* Finish building, and return the root object. Will not be NULL. */
 QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 {
-    /* FIXME: we should require that a visit occurred, and that it is
-     * complete (no starts without a matching end) */
-    QObject *obj = qov->root;
-    if (obj) {
-        qobject_incref(obj);
-    } else {
-        obj = qnull();
-    }
-    return obj;
+    QObject *root;
+
+    assert(qov->root);              /* A visit must have occurred...  */
+    assert(!qmp_output_last(qov));  /* ...with each start paired with end.  */
+    root = qov->root;
+    qov->root = NULL;
+    return root;
 }

 Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
@@ -221,7 +224,7 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
     return &v->visitor;
 }

-void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+void qmp_output_visitor_reset(QmpOutputVisitor *v)
 {
     QStackEntry *e, *tmp;

@@ -231,6 +234,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
     }

     qobject_decref(v->root);
+    v->root = NULL;
+}
+
+void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+{
+    qmp_output_visitor_reset(v);
     g_free(v);
 }

@@ -252,6 +261,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.type_str = qmp_output_type_str;
     v->visitor.type_number = qmp_output_type_number;
     v->visitor.type_any = qmp_output_type_any;
+    v->visitor.type_null = qmp_output_type_null;

     QTAILQ_INIT(&v->stack);

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 26dc752..74d0ac4 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -260,6 +260,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
         visit_type_UserDefOne(data->ov, "unused", &pu, &err);
         g_assert(err);
         error_free(err);
+        qmp_output_visitor_reset(data->qov);
     }
 }

@@ -459,6 +460,7 @@ static void test_visitor_out_empty(TestOutputVisitorData *data,
 {
     QObject *arg;

+    visit_type_null(data->ov, NULL, &error_abort);
     arg = qmp_output_get_qobject(data->qov);
     g_assert(qobject_type(arg) == QTYPE_QNULL);
     /* Check that qnull reference counting is sane */
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (23 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-22 19:15   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
                   ` (12 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: marcandre.lureau, Alexander Graf, open list:sPAPR, armbru, David Gibson

Now that the QMP output visitor supports an explicit null
output, we should utilize it to make it easier to diagnose
the difference between a missing fdt vs. a present-but-empty
one.

(Note that this reverts the behavior of commit ab8bf1d, taking
us back to the behavior of commit 6c2f9a1 [which in turn
stemmed from a crash fix in 1d10b44]; but that this time,
the change is intentional and not an accidental side-effect.)

Signed-off-by: Eric Blake <eblake@redhat.com>
Acked-by: David Gibson <david@gibson.dropbear.id.au>

---
v9: improved commit message
v8: rebase to 'name' motion
v7: new patch, based on discussion about spapr_drc.c
---
 hw/ppc/spapr_drc.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index ffc2cd9..831ce23 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -259,11 +259,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
     void *fdt;

     if (!drc->fdt) {
-        visit_start_struct(v, name, NULL, 0, &err);
-        if (!err) {
-            visit_end_struct(v, &err);
-        }
-        error_propagate(errp, err);
+        visit_type_null(v, NULL, errp);
         return;
     }

-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (24 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-22 19:24   ` Markus Armbruster
  2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper Eric Blake
                   ` (11 subsequent siblings)
  37 siblings, 2 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

When reporting that an unvisited member remains at the end of an
input visit for a struct, we were using g_hash_table_find()
coupled with a callback function that always returns true, to
locate an arbitrary member of the hash table.  But if all we
need is an arbitrary entry, we can get that from a single-use
iterator, without needing a tautological callback function.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to earlier changes
v7: retitle, rebase to earlier context changes
v6: new patch, based on comments on RFC against v5 7/46
---
 qapi/opts-visitor.c      | 12 +++---------
 qapi/qmp-input-visitor.c | 14 +++++---------
 2 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 62ffdd4..df312e6 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -156,17 +156,11 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
 }


-static gboolean
-ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
-{
-    return TRUE;
-}
-
-
 static void
 opts_end_struct(Visitor *v, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
+    GHashTableIter iter;
     GQueue *any;

     if (--ov->depth > 0) {
@@ -174,8 +168,8 @@ opts_end_struct(Visitor *v, Error **errp)
     }

     /* we should have processed all (distinct) QemuOpt instances */
-    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
-    if (any) {
+    g_hash_table_iter_init(&iter, ov->unprocessed_opts);
+    if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
         const QemuOpt *first;

         first = g_queue_peek_head(any);
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index ad23bec..91ef945 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -88,12 +88,6 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
     qiv->nb_stack++;
 }

-/** Only for qmp_input_pop. */
-static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey)
-{
-    *(const char **)user_pkey = (const char *)key;
-    return TRUE;
-}

 static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
 {
@@ -102,9 +96,11 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
     if (qiv->strict) {
         GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
         if (top_ht) {
-            if (g_hash_table_size(top_ht)) {
-                const char *key;
-                g_hash_table_find(top_ht, always_true, &key);
+            GHashTableIter iter;
+            const char *key;
+
+            g_hash_table_iter_init(&iter, top_ht);
+            if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
                 error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
             }
             g_hash_table_unref(top_ht);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (25 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-25 14:15   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type Eric Blake
                   ` (10 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

And use it in qapi-types and qapi-event.  Down the road, we may
want to lift our artificial restriction of no variants at the
top level of an event, at which point, inlining our check for
whether members is empty will no longer be sufficient.  More
immediately, the new .is_empty() helper will help fix a bug in
qapi-visit in the next patch, where the generator did not handle
an explicit but empty type in the same was as a missing type.

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

---
v9: improve commit message
v8: no change
v7: rebase to context change
v6: new patch
---
 scripts/qapi-event.py | 6 +++---
 scripts/qapi-types.py | 2 +-
 scripts/qapi.py       | 3 +++
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index a1dca29..56c93a8 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -39,7 +39,7 @@ def gen_event_send(name, arg_type):
 ''',
                 proto=gen_event_send_proto(name, arg_type))

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     QmpOutputVisitor *qov;
     Visitor *v;
@@ -58,7 +58,7 @@ def gen_event_send(name, arg_type):
 ''',
                  name=name)

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     qov = qmp_output_visitor_new();
     v = qmp_output_get_visitor(qov);
@@ -88,7 +88,7 @@ out_obj:
 ''',
                  c_enum=c_enum_const(event_enum_name, name))

-    if arg_type and arg_type.members:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
 out:
     qmp_output_visitor_cleanup(qov);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index d3f631a..c70fae1 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -76,7 +76,7 @@ struct %(c_name)s {
     # potential issues with attempting to malloc space for zero-length
     # structs in C, and also incompatibility with C++ (where an empty
     # struct is size 1).
-    if not (base and base.members) and not members and not variants:
+    if (not base or base.is_empty()) and not members and not variants:
         ret += mcgen('''
     char qapi_dummy_field_for_empty_struct;
 ''')
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 3b4c62e..a13c110 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -973,6 +973,9 @@ class QAPISchemaObjectType(QAPISchemaType):
         # See QAPISchema._make_implicit_object_type()
         return self.name[0] == ':'

+    def is_empty(self):
+        return not self.members and not self.variants
+
     def c_name(self):
         assert not self.is_implicit()
         return QAPISchemaType.c_name(self)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (26 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-25 15:03   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
                   ` (9 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

The generator special-cased
 { 'command':'foo', 'data': {} }
to avoid emitting a visitor variable, but failed to see that
 { 'struct':'NamedEmptyType, 'data': {} }
 { 'command':'foo', 'data':'NamedEmptyType' }
needs the same treatment.  Without a fix to the generator, the
change to qapi-schema-test.json fails to compile with:

tests/test-qmp-marshal.c: In function ‘qmp_marshal_user_def_cmd0’:
tests/test-qmp-marshal.c:264:14: error: variable ‘v’ set but not used [-Werror=unused-but-set-variable]
     Visitor *v;
              ^

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: no change
v7: no change
v6: new patch
---
 scripts/qapi-commands.py                | 6 +++---
 tests/qapi-schema/qapi-schema-test.json | 2 ++
 tests/qapi-schema/qapi-schema-test.out  | 2 ++
 tests/test-qmp-commands.c               | 5 +++++
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 91c5a4e..00ee565 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -65,7 +65,7 @@ def gen_marshal_vars(arg_type, ret_type):
 ''',
                      c_type=ret_type.c_type())

-    if arg_type:
+    if arg_type and not arg_type.is_empty():
         ret += mcgen('''
     QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
     QapiDeallocVisitor *qdv;
@@ -97,7 +97,7 @@ def gen_marshal_vars(arg_type, ret_type):
 def gen_marshal_input_visit(arg_type, dealloc=False):
     ret = ''

-    if not arg_type:
+    if not arg_type or arg_type.is_empty():
         return ret

     if dealloc:
@@ -177,7 +177,7 @@ def gen_marshal(name, arg_type, ret_type):

     # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
     # for each arg_type member, and by gen_call() for ret_type
-    if (arg_type and arg_type.members) or ret_type:
+    if (arg_type and not arg_type.is_empty()) or ret_type:
         ret += mcgen('''

 out:
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 4b89527..a0fdb88 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -18,6 +18,8 @@
 { 'struct': 'Empty1', 'data': { } }
 { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }

+{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
+
 # for testing override of default naming heuristic
 { 'enum': 'QEnumTwo',
   'prefix': 'QENUM_TWO',
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 2c546b7..d8f9108 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -198,6 +198,8 @@ command guest-sync :obj-guest-sync-arg -> any
    gen=True success_response=True
 command user_def_cmd None -> None
    gen=True success_response=True
+command user_def_cmd0 Empty2 -> Empty2
+   gen=True success_response=True
 command user_def_cmd1 :obj-user_def_cmd1-arg -> None
    gen=True success_response=True
 command user_def_cmd2 :obj-user_def_cmd2-arg -> UserDefTwo
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 4d267b6..bc8085d 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -12,6 +12,11 @@ void qmp_user_def_cmd(Error **errp)
 {
 }

+Empty2 *qmp_user_def_cmd0(Error **errp)
+{
+    return g_new0(Empty2, 1);
+}
+
 void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
 {
 }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (27 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-25 17:04   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty Eric Blake
                   ` (8 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

For empty structs, such as the 'Abort' helper type used as part
of the 'transaction' command, we were emitting a no-op
visit_type_FOO_fields().  Optimize things to instead omit calls
for empty structs.  Generated code changes resemble:

|-static void visit_type_Abort_fields(Visitor *v, Abort **obj, Error **errp)
|-{
|-    Error *err = NULL;
|-
|-    error_propagate(errp, err);
|-}
|-
| void visit_type_Abort(Visitor *v, const char *name, Abort **obj, Error **errp)
| {
|     Error *err = NULL;
|@@ -112,7 +105,6 @@ void visit_type_Abort(Visitor *v, Abort
|     if (!*obj) {
|         goto out_obj;
|     }
|-    visit_type_Abort_fields(v, obj, &err);
| out_obj:
|     error_propagate(errp, err);

Another reason for doing this optimization is that it gets us
closer to merging the code for visiting structs and unions:
since flat unions have no local members, they do not need to
have a visit_type_UNION_fields() emitted, even when they start
sharing the code used to visit structs.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to earlier changes
v7: rebase to earlier changes
v6: new patch
---
 scripts/qapi-visit.py | 41 +++++++++++++++++++++++------------------
 1 file changed, 23 insertions(+), 18 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 573bb81..6537a20 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -35,22 +35,22 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **


 def gen_visit_fields_decl(typ):
-    ret = ''
-    if typ.name not in struct_fields_seen:
-        ret += mcgen('''
+    if typ.is_empty() or typ.name in struct_fields_seen:
+        return ''
+
+    struct_fields_seen.add(typ.name)
+    return mcgen('''

 static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
 ''',
-                     c_type=typ.c_name())
-        struct_fields_seen.add(typ.name)
-    return ret
+                 c_type=typ.c_name())


 def gen_visit_implicit_struct(typ):
-    if typ in implicit_structs_seen:
+    if typ.is_empty() or typ in implicit_structs_seen:
         return ''
+
     implicit_structs_seen.add(typ)
-
     ret = gen_visit_fields_decl(typ)

     ret += mcgen('''
@@ -74,7 +74,10 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
 def gen_visit_struct_fields(name, base, members):
     ret = ''

-    if base:
+    if (not base or base.is_empty()) and not members:
+        return ret
+
+    if base and not base.is_empty():
         ret += gen_visit_fields_decl(base)

     struct_fields_seen.add(name)
@@ -87,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
 ''',
                  c_name=c_name(name))

-    if base:
+    if base and not base.is_empty():
         ret += mcgen('''
     visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
 ''',
@@ -96,13 +99,9 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e

     ret += gen_visit_fields(members, prefix='(*obj)->')

-    # 'goto out' produced for base, and by gen_visit_fields() for each member
-    if base or members:
-        ret += mcgen('''
+    ret += mcgen('''

 out:
-''')
-    ret += mcgen('''
     error_propagate(errp, err);
 }
 ''')
@@ -129,7 +128,14 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     if (!*obj) {
         goto out_obj;
     }
+''',
+                 name=name, c_name=c_name(name))
+    if (base and not base.is_empty()) or members:
+        ret += mcgen('''
     visit_type_%(c_name)s_fields(v, obj, &err);
+''',
+                     c_name=c_name(name))
+    ret += mcgen('''
 out_obj:
     error_propagate(errp, err);
     err = NULL;
@@ -137,8 +143,7 @@ out_obj:
 out:
     error_propagate(errp, err);
 }
-''',
-                 c_name=c_name(name))
+''')

     return ret

@@ -300,7 +305,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
 ''',
                          c_type=simple_union_type.c_name(),
                          c_name=c_name(var.name))
-        else:
+        elif not var.type.is_empty():
             ret += mcgen('''
         visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
 ''',
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (28 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-25 19:04   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit Eric Blake
                   ` (7 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Now that we elide unnecessary visits of empty types, we can
start using the special ':empty' type in more places.  By using
the empty type as the base class of every explicit struct or
union, and as the default data for any command or event, we can
simplify later logic in qapi-{visit,commands,event} by merely
checking whether the type is empty, without also having to worry
whether a type was even supplied.

Note that gen_object() in qapi-types still has to check for a
base, because it is also called for alternates (which have no
base).

No change to generated code.

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

---
v9: squash in more related changes
v8: rebase to earlier changes
v7: rebase to earlier changes
v6: new patch
---
 scripts/qapi-commands.py                | 17 +++++++------
 scripts/qapi-event.py                   |  5 ++--
 scripts/qapi-types.py                   |  4 +--
 scripts/qapi-visit.py                   | 12 +++++----
 scripts/qapi.py                         | 25 +++++++++---------
 tests/qapi-schema/event-case.out        |  2 +-
 tests/qapi-schema/flat-union-empty.out  |  1 +
 tests/qapi-schema/ident-with-escape.out |  1 +
 tests/qapi-schema/indented-expr.out     |  4 +--
 tests/qapi-schema/qapi-schema-test.out  | 45 ++++++++++++++++++++++++++++++---
 tests/qapi-schema/union-clash-data.out  |  2 ++
 tests/qapi-schema/union-empty.out       |  1 +
 12 files changed, 83 insertions(+), 36 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 00ee565..9d455c3 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -29,11 +29,11 @@ def gen_call(name, arg_type, ret_type):
     ret = ''

     argstr = ''
-    if arg_type:
-        for memb in arg_type.members:
-            if memb.optional:
-                argstr += 'has_%s, ' % c_name(memb.name)
-            argstr += '%s, ' % c_name(memb.name)
+    assert arg_type
+    for memb in arg_type.members:
+        if memb.optional:
+            argstr += 'has_%s, ' % c_name(memb.name)
+        argstr += '%s, ' % c_name(memb.name)

     lhs = ''
     if ret_type:
@@ -65,7 +65,8 @@ def gen_marshal_vars(arg_type, ret_type):
 ''',
                      c_type=ret_type.c_type())

-    if arg_type and not arg_type.is_empty():
+    assert arg_type
+    if not arg_type.is_empty():
         ret += mcgen('''
     QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
     QapiDeallocVisitor *qdv;
@@ -97,7 +98,7 @@ def gen_marshal_vars(arg_type, ret_type):
 def gen_marshal_input_visit(arg_type, dealloc=False):
     ret = ''

-    if not arg_type or arg_type.is_empty():
+    if arg_type.is_empty():
         return ret

     if dealloc:
@@ -177,7 +178,7 @@ def gen_marshal(name, arg_type, ret_type):

     # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
     # for each arg_type member, and by gen_call() for ret_type
-    if (arg_type and not arg_type.is_empty()) or ret_type:
+    if not arg_type.is_empty() or ret_type:
         ret += mcgen('''

 out:
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 56c93a8..cc55de7 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -39,7 +39,8 @@ def gen_event_send(name, arg_type):
 ''',
                 proto=gen_event_send_proto(name, arg_type))

-    if arg_type and not arg_type.is_empty():
+    assert arg_type
+    if not arg_type.is_empty():
         ret += mcgen('''
     QmpOutputVisitor *qov;
     Visitor *v;
@@ -88,7 +89,7 @@ out_obj:
 ''',
                  c_enum=c_enum_const(event_enum_name, name))

-    if arg_type and not arg_type.is_empty():
+    if not arg_type.is_empty():
         ret += mcgen('''
 out:
     qmp_output_visitor_cleanup(qov);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index c70fae1..5cf20c2 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -58,7 +58,7 @@ struct %(c_name)s {
 ''',
                 c_name=c_name(name))

-    if base:
+    if base and not base.is_empty():
         ret += mcgen('''
     /* Members inherited from %(c_name)s: */
 ''',
@@ -222,7 +222,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
     def visit_object_type(self, name, info, base, members, variants):
         self._fwdecl += gen_fwd_object_or_array(name)
         self.decl += gen_object(name, base, members, variants)
-        if base:
+        if not base.is_implicit():
             self.decl += gen_upcast(name, base)
         self._gen_type_cleanup(name)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 6537a20..6d5c3d9 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -74,10 +74,11 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
 def gen_visit_struct_fields(name, base, members):
     ret = ''

-    if (not base or base.is_empty()) and not members:
+    assert base
+    if base.is_empty() and not members:
         return ret

-    if base and not base.is_empty():
+    if not base.is_empty():
         ret += gen_visit_fields_decl(base)

     struct_fields_seen.add(name)
@@ -90,7 +91,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
 ''',
                  c_name=c_name(name))

-    if base and not base.is_empty():
+    if not base.is_empty():
         ret += mcgen('''
     visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
 ''',
@@ -246,7 +247,8 @@ out:
 def gen_visit_union(name, base, variants):
     ret = ''

-    if base:
+    assert base
+    if not base.is_empty():
         ret += gen_visit_fields_decl(base)

     for var in variants.variants:
@@ -270,7 +272,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
 ''',
                  c_name=c_name(name))

-    if base:
+    if not base.is_empty():
         ret += mcgen('''
     visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
 ''',
diff --git a/scripts/qapi.py b/scripts/qapi.py
index a13c110..e9006e4 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -922,11 +922,11 @@ class QAPISchemaArrayType(QAPISchemaType):

 class QAPISchemaObjectType(QAPISchemaType):
     def __init__(self, name, info, base, local_members, variants):
-        # struct has local_members, optional base, and no variants
+        # struct has local_members, base, and no variants
         # flat union has base, variants, and no local_members
-        # simple union has local_members, variants, and no base
+        # simple union has local_members, variants, and base of :empty
         QAPISchemaType.__init__(self, name, info)
-        assert base is None or isinstance(base, str)
+        assert isinstance(base, str) or name == ':empty'
         for m in local_members:
             assert isinstance(m, QAPISchemaObjectTypeMember)
             m.set_owner(name)
@@ -1264,11 +1264,11 @@ class QAPISchema(object):

     def _make_implicit_object_type(self, name, info, role, members):
         if not members:
-            return None
+            return ':empty'
         # See also QAPISchemaObjectTypeMember._pretty_owner()
         name = ':obj-%s-%s' % (name, role)
         if not self.lookup_entity(name, QAPISchemaObjectType):
-            self._def_entity(QAPISchemaObjectType(name, info, None,
+            self._def_entity(QAPISchemaObjectType(name, info, ':empty',
                                                   members, None))
         return name

@@ -1295,7 +1295,7 @@ class QAPISchema(object):

     def _def_struct_type(self, expr, info):
         name = expr['struct']
-        base = expr.get('base')
+        base = expr.get('base', ':empty')
         data = expr['data']
         self._def_entity(QAPISchemaObjectType(name, info, base,
                                               self._make_members(data, info),
@@ -1315,7 +1315,7 @@ class QAPISchema(object):
     def _def_union_type(self, expr, info):
         name = expr['union']
         data = expr['data']
-        base = expr.get('base')
+        base = expr.get('base', ':empty')
         tag_name = expr.get('discriminator')
         tag_member = None
         if tag_name:
@@ -1349,11 +1349,11 @@ class QAPISchema(object):

     def _def_command(self, expr, info):
         name = expr['command']
-        data = expr.get('data')
+        data = expr.get('data', {})
         rets = expr.get('returns')
         gen = expr.get('gen', True)
         success_response = expr.get('success-response', True)
-        if isinstance(data, OrderedDict):
+        if isinstance(data, dict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
         if isinstance(rets, list):
@@ -1364,8 +1364,8 @@ class QAPISchema(object):

     def _def_event(self, expr, info):
         name = expr['event']
-        data = expr.get('data')
-        if isinstance(data, OrderedDict):
+        data = expr.get('data', {})
+        if isinstance(data, dict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
         self._def_entity(QAPISchemaEvent(name, info, data))
@@ -1612,8 +1612,7 @@ extern const char *const %(c_name)s_lookup[];


 def gen_params(arg_type, extra):
-    if not arg_type:
-        return extra
+    assert arg_type
     assert not arg_type.variants
     ret = ''
     sep = ''
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index 6350d64..18866d9 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,4 +1,4 @@
 object :empty
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
-event oops None
+event oops :empty
diff --git a/tests/qapi-schema/flat-union-empty.out b/tests/qapi-schema/flat-union-empty.out
index eade2d5..1a58e07 100644
--- a/tests/qapi-schema/flat-union-empty.out
+++ b/tests/qapi-schema/flat-union-empty.out
@@ -1,5 +1,6 @@
 object :empty
 object Base
+    base :empty
     member type: Empty optional=False
 enum Empty []
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 453e0b2..142cd19 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,5 +1,6 @@
 object :empty
 object :obj-fooA-arg
+    base :empty
     member bar1: str optional=False
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index ce37ff5..5c25fcd 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,7 +1,7 @@
 object :empty
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
-command eins None -> None
+command eins :empty -> None
    gen=True success_response=True
-command zwei None -> None
+command zwei :empty -> None
    gen=True success_response=True
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index d8f9108..a2a3c8b 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,56 +1,78 @@
 object :empty
 object :obj-EVENT_C-arg
+    base :empty
     member a: int optional=True
     member b: UserDefOne optional=True
     member c: str optional=False
 object :obj-EVENT_D-arg
+    base :empty
     member a: EventStructOne optional=False
     member b: str optional=False
     member c: str optional=True
     member enum3: EnumOne optional=True
 object :obj-__org.qemu_x-command-arg
+    base :empty
     member a: __org.qemu_x-EnumList optional=False
     member b: __org.qemu_x-StructList optional=False
     member c: __org.qemu_x-Union2 optional=False
     member d: __org.qemu_x-Alt optional=False
 object :obj-anyList-wrapper
+    base :empty
     member data: anyList optional=False
 object :obj-boolList-wrapper
+    base :empty
     member data: boolList optional=False
 object :obj-guest-get-time-arg
+    base :empty
     member a: int optional=False
     member b: int optional=True
 object :obj-guest-sync-arg
+    base :empty
     member arg: any optional=False
 object :obj-int16List-wrapper
+    base :empty
     member data: int16List optional=False
 object :obj-int32List-wrapper
+    base :empty
     member data: int32List optional=False
 object :obj-int64List-wrapper
+    base :empty
     member data: int64List optional=False
 object :obj-int8List-wrapper
+    base :empty
     member data: int8List optional=False
 object :obj-intList-wrapper
+    base :empty
     member data: intList optional=False
 object :obj-numberList-wrapper
+    base :empty
     member data: numberList optional=False
 object :obj-sizeList-wrapper
+    base :empty
     member data: sizeList optional=False
 object :obj-str-wrapper
+    base :empty
     member data: str optional=False
 object :obj-strList-wrapper
+    base :empty
     member data: strList optional=False
 object :obj-uint16List-wrapper
+    base :empty
     member data: uint16List optional=False
 object :obj-uint32List-wrapper
+    base :empty
     member data: uint32List optional=False
 object :obj-uint64List-wrapper
+    base :empty
     member data: uint64List optional=False
 object :obj-uint8List-wrapper
+    base :empty
     member data: uint8List optional=False
 object :obj-user_def_cmd1-arg
+    base :empty
     member ud1a: UserDefOne optional=False
 object :obj-user_def_cmd2-arg
+    base :empty
     member ud1a: UserDefOne optional=False
     member ud1b: UserDefOne optional=True
 alternate AltIntNum
@@ -71,24 +93,28 @@ alternate AltStrInt
 alternate AltStrNum
     case s: str
     case n: number
-event EVENT_A None
-event EVENT_B None
+event EVENT_A :empty
+event EVENT_B :empty
 event EVENT_C :obj-EVENT_C-arg
 event EVENT_D :obj-EVENT_D-arg
 object Empty1
+    base :empty
 object Empty2
     base Empty1
 enum EnumOne ['value1', 'value2', 'value3']
 object EventStructOne
+    base :empty
     member struct1: UserDefOne optional=False
     member string: str optional=False
     member enum2: EnumOne optional=True
 object ForceArrays
+    base :empty
     member unused1: UserDefOneList optional=False
     member unused2: UserDefTwoList optional=False
     member unused3: TestStructList optional=False
 enum MyEnum []
 object NestedEnumsOne
+    base :empty
     member enum1: EnumOne optional=False
     member enum2: EnumOne optional=True
     member enum3: EnumOne optional=False
@@ -98,10 +124,12 @@ enum QEnumTwo ['value1', 'value2']
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
 object TestStruct
+    base :empty
     member integer: int optional=False
     member boolean: bool optional=False
     member string: str optional=False
 object UserDefA
+    base :empty
     member boolean: bool optional=False
     member a_b: int optional=True
 alternate UserDefAlternate
@@ -109,9 +137,11 @@ alternate UserDefAlternate
     case s: str
     case i: int
 object UserDefB
+    base :empty
     member intb: int optional=False
     member a-b: bool optional=True
 object UserDefC
+    base :empty
     member string1: str optional=False
     member string2: str optional=False
 object UserDefFlatUnion
@@ -127,6 +157,7 @@ object UserDefFlatUnion2
     case value2: UserDefB
     case value3: UserDefA
 object UserDefNativeListUnion
+    base :empty
     member type: UserDefNativeListUnionKind optional=False
     case integer: :obj-intList-wrapper
     case s8: :obj-int8List-wrapper
@@ -148,19 +179,23 @@ object UserDefOne
     member string: str optional=False
     member enum1: EnumOne optional=True
 object UserDefOptions
+    base :empty
     member i64: intList optional=True
     member u64: uint64List optional=True
     member u16: uint16List optional=True
     member i64x: int optional=True
     member u64x: uint64 optional=True
 object UserDefTwo
+    base :empty
     member string0: str optional=False
     member dict1: UserDefTwoDict optional=False
 object UserDefTwoDict
+    base :empty
     member string1: str optional=False
     member dict2: UserDefTwoDictDict optional=False
     member dict3: UserDefTwoDictDict optional=True
 object UserDefTwoDictDict
+    base :empty
     member userdef: UserDefOne optional=False
     member string: str optional=False
 object UserDefUnionBase
@@ -168,12 +203,14 @@ object UserDefUnionBase
     member string: str optional=False
     member enum1: EnumOne optional=False
 object UserDefZero
+    base :empty
     member integer: int optional=False
 event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
 alternate __org.qemu_x-Alt
     case __org.qemu_x-branch: str
     case b: __org.qemu_x-Base
 object __org.qemu_x-Base
+    base :empty
     member __org.qemu_x-member1: __org.qemu_x-Enum optional=False
 enum __org.qemu_x-Enum ['__org.qemu_x-value']
 object __org.qemu_x-Struct
@@ -181,8 +218,10 @@ object __org.qemu_x-Struct
     member __org.qemu_x-member2: str optional=False
     member wchar-t: int optional=True
 object __org.qemu_x-Struct2
+    base :empty
     member array: __org.qemu_x-Union1List optional=False
 object __org.qemu_x-Union1
+    base :empty
     member type: __org.qemu_x-Union1Kind optional=False
     case __org.qemu_x-branch: :obj-str-wrapper
 enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
@@ -196,7 +235,7 @@ command guest-get-time :obj-guest-get-time-arg -> int
    gen=True success_response=True
 command guest-sync :obj-guest-sync-arg -> any
    gen=True success_response=True
-command user_def_cmd None -> None
+command user_def_cmd :empty -> None
    gen=True success_response=True
 command user_def_cmd0 Empty2 -> Empty2
    gen=True success_response=True
diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out
index f5752f4..19031a2 100644
--- a/tests/qapi-schema/union-clash-data.out
+++ b/tests/qapi-schema/union-clash-data.out
@@ -1,9 +1,11 @@
 object :empty
 object :obj-int-wrapper
+    base :empty
     member data: int optional=False
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
 object TestUnion
+    base :empty
     member type: TestUnionKind optional=False
     case data: :obj-int-wrapper
 enum TestUnionKind ['data']
diff --git a/tests/qapi-schema/union-empty.out b/tests/qapi-schema/union-empty.out
index bdf17e5..57fcbd1 100644
--- a/tests/qapi-schema/union-empty.out
+++ b/tests/qapi-schema/union-empty.out
@@ -2,5 +2,6 @@ object :empty
 enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
     prefix QTYPE
 object Union
+    base :empty
     member type: UnionKind optional=False
 enum UnionKind []
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (29 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-27 14:12   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct Eric Blake
                   ` (6 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

We are finally at the point where gen_visit_struct() and
gen_visit_union() can be unified to a generic gen_visit_object().

The generated code for structs and for flat unions is unchanged.
For simple unions, a new visit_type_FOO_fields() is created,
wrapping the visit of the non-variant tag field:

|+static void visit_type_ChardevBackend_fields(Visitor *v, ChardevBackend **obj, Error **errp)
|+{
|+    Error *err = NULL;
|+
|+    visit_type_ChardevBackendKind(v, "type", &(*obj)->type, &err);
|+    if (err) {
|+        goto out;
|+    }
|+
|+out:
|+    error_propagate(errp, err);
|+}
|+
| void visit_type_ChardevBackend(Visitor *v, const char *name, ChardevBackend **obj, Error **errp)
| {
|     Error *err = NULL;
|@@ -2319,7 +2332,7 @@ void visit_type_ChardevBackend(Visitor *
|     if (!*obj) {
|         goto out_obj;
|     }
|-    visit_type_ChardevBackendKind(v, "type", &(*obj)->type, &err);
|+    visit_type_ChardevBackend_fields(v, obj, &err);
|     if (err) {

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to 'name' motion
v7: rebase to earlier changes
v6: new patch
---
 scripts/qapi-visit.py | 133 +++++++++++++++++++-------------------------------
 1 file changed, 51 insertions(+), 82 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 6d5c3d9..feef17f 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -109,46 +109,6 @@ out:
     return ret


-def gen_visit_struct(name, base, members):
-    ret = gen_visit_struct_fields(name, base, members)
-
-    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
-    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
-    # rather than leaving it non-NULL. As currently written, the caller must
-    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
-    ret += mcgen('''
-
-void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
-{
-    Error *err = NULL;
-
-    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
-    if (err) {
-        goto out;
-    }
-    if (!*obj) {
-        goto out_obj;
-    }
-''',
-                 name=name, c_name=c_name(name))
-    if (base and not base.is_empty()) or members:
-        ret += mcgen('''
-    visit_type_%(c_name)s_fields(v, obj, &err);
-''',
-                     c_name=c_name(name))
-    ret += mcgen('''
-out_obj:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, &err);
-out:
-    error_propagate(errp, err);
-}
-''')
-
-    return ret
-
-
 def gen_visit_list(name, element_type):
     # FIXME: if *obj is NULL on entry, and the first visit_next_list()
     # assigns to *obj, while a later one fails, we should clean up *obj
@@ -244,18 +204,24 @@ out:
     return ret


-def gen_visit_union(name, base, variants):
+def gen_visit_object(name, base, members, variants):
     ret = ''

     assert base
     if not base.is_empty():
         ret += gen_visit_fields_decl(base)
+    if members:
+        ret += gen_visit_struct_fields(name, base, members)
+    if variants:
+        for var in variants.variants:
+            # Ugly special case for simple union TODO get rid of it
+            if not var.simple_union_type():
+                ret += gen_visit_implicit_struct(var.type)

-    for var in variants.variants:
-        # Ugly special case for simple union TODO get rid of it
-        if not var.simple_union_type():
-            ret += gen_visit_implicit_struct(var.type)
-
+    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
+    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
+    # rather than leaving it non-NULL. As currently written, the caller must
+    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
     ret += mcgen('''

 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
@@ -272,61 +238,71 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
 ''',
                  c_name=c_name(name))

-    if not base.is_empty():
+    if not base.is_empty() or members:
+        if members:
+            type_name = c_name(name)
+            cast = ''
+        else:
+            type_name = base.c_name()
+            cast = '(%s **)' % type_name
         ret += mcgen('''
-    visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
+    visit_type_%(c_name)s_fields(v, %(cast)sobj, &err);
 ''',
-                     c_name=base.c_name())
-    else:
+                     c_name=type_name, cast=cast)
+        if variants:
+            ret += gen_err_check(label='out_obj')
+
+    if variants:
         ret += mcgen('''
-    visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
-''',
-                     c_type=variants.tag_member.type.c_name(),
-                     c_name=c_name(variants.tag_member.name),
-                     name=variants.tag_member.name)
-    ret += gen_err_check(label='out_obj')
-    ret += mcgen('''
     if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
         goto out_obj;
     }
     switch ((*obj)->%(c_name)s) {
 ''',
-                 c_name=c_name(variants.tag_member.name))
+                     c_name=c_name(variants.tag_member.name))

-    for var in variants.variants:
-        # TODO ugly special case for simple union
-        simple_union_type = var.simple_union_type()
-        ret += mcgen('''
+        for var in variants.variants:
+            # TODO ugly special case for simple union
+            simple_union_type = var.simple_union_type()
+            ret += mcgen('''
     case %(case)s:
 ''',
-                     case=c_enum_const(variants.tag_member.type.name,
-                                       var.name))
-        if simple_union_type:
-            ret += mcgen('''
+                         case=c_enum_const(variants.tag_member.type.name,
+                                           var.name))
+            if simple_union_type:
+                ret += mcgen('''
         visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
 ''',
-                         c_type=simple_union_type.c_name(),
-                         c_name=c_name(var.name))
-        elif not var.type.is_empty():
-            ret += mcgen('''
+                             c_type=simple_union_type.c_name(),
+                             c_name=c_name(var.name))
+            elif not var.type.is_empty():
+                ret += mcgen('''
         visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
 ''',
-                         c_type=var.type.c_name(),
-                         c_name=c_name(var.name))
-        ret += mcgen('''
+                             c_type=var.type.c_name(),
+                             c_name=c_name(var.name))
+            ret += mcgen('''
         break;
 ''')

-    ret += mcgen('''
+        ret += mcgen('''
     default:
         abort();
     }
+''')
+
+    ret += mcgen('''
 out_obj:
+''')
+    if variants:
+        ret += mcgen('''
     error_propagate(errp, err);
     err = NULL;
     if (*obj) {
         visit_end_union(v, !!(*obj)->u.data, &err);
     }
+''')
+    ret += mcgen('''
     error_propagate(errp, err);
     err = NULL;
     visit_end_struct(v, &err);
@@ -387,14 +363,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):

     def visit_object_type(self, name, info, base, members, variants):
         self.decl += gen_visit_decl(name)
-        if variants:
-            if members:
-                # Members other than variants.tag_member not implemented
-                assert len(members) == 1
-                assert members[0] == variants.tag_member
-            self.defn += gen_visit_union(name, base, variants)
-        else:
-            self.defn += gen_visit_struct(name, base, members)
+        self.defn += gen_visit_object(name, base, members, variants)

     def visit_alternate_type(self, name, info, variants):
         self.decl += gen_visit_decl(name)
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (30 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-27 16:41   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces Eric Blake
                   ` (5 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Commit cee2dedb noticed that if you have a partial flat union
(such as if an input parse failed due to a missing
discriminator), calling the dealloc visitor could result in
trying to dereference the NULL pointer. But the fix it proposed
requires the use of a 'data' member in the union, which may or
may not be the same size as other branches of the union
(consider a 32-bit platform where one of the branches is an
int64), so it feels fairly dirty.  A better fix is to tweak all
of the generated visit_type_implicit_FOO() functions to avoid
dereferencing NULL in the first place, by not visiting the
fields if the struct pointer itself is not present, at which
point we no longer even need visit_start_union().  And no one
was implementing visit_end_union() callbacks.

While rewriting the code, use patterns that are closer to what
is used elsewhere in the generated visitors, by using 'goto'
to cleanup labels rather than putting followup code under 'if'
conditions.  The change keeps the contract that any successful
use of visit_start_implicit_struct() will be paired with a
matching visit_end_implicit_struct(), even if intermediate
processing is skipped.  We are safe in checking *obj alone, as
as the contract of visit_start_implicit_struct() requires a
non-NULL obj.

As an example of the changes to generated code:
|@@ -1331,10 +1331,16 @@ static void visit_type_implicit_Blockdev
|     Error *err = NULL;
|
|     visit_start_implicit_struct(v, (void **)obj, sizeof(BlockdevOptionsArchipelago), &err);
|-    if (!err) {
|-        visit_type_BlockdevOptionsArchipelago_fields(v, obj, errp);
|-        visit_end_implicit_struct(v);
|+    if (err) {
|+        goto out;
|+    }
|+    if (!*obj) {
|+        goto out_obj;
|     }
|+    visit_type_BlockdevOptionsArchipelago_fields(v, obj, &err);
|+out_obj:
|+    visit_end_implicit_struct(v);
|+out:
|     error_propagate(errp, err);
| }
...
|@@ -1479,9 +1539,6 @@ void visit_type_BlockdevOptions(Visitor
|     if (err) {
|         goto out_obj;
|     }
|-    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
|-        goto out_obj;
|-    }
|     switch ((*obj)->driver) {
|     case BLOCKDEV_DRIVER_ARCHIPELAGO:
|         visit_type_implicit_BlockdevOptionsArchipelago(v, &(*obj)->u.archipelago, &err);
|@@ -1570,11 +1627,6 @@ void visit_type_BlockdevOptions(Visitor
| out_obj:
|     error_propagate(errp, err);
|     err = NULL;
|-    if (*obj) {
|-        visit_end_union(v, !!(*obj)->u.data, &err);
|-    }
|-    error_propagate(errp, err);
|-    err = NULL;
|     visit_end_struct(v, &err);

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: rebase to 'name' motion
v7: rebase to earlier context changes, simplify 'obj && !*obj'
condition based on contract
v6: rebase due to deferring 7/46, and gen_err_check() improvements;
rewrite gen_visit_implicit_struct() more like other patterns
---
 include/qapi/visitor-impl.h |  5 -----
 include/qapi/visitor.h      | 12 ------------
 qapi/qapi-dealloc-visitor.c | 26 --------------------------
 qapi/qapi-visit-core.c      | 15 ---------------
 scripts/qapi-visit.py       | 25 +++++++++----------------
 5 files changed, 9 insertions(+), 74 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 913f1b0..3b68b7b 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -81,11 +81,6 @@ struct Visitor

     /* May be NULL; most useful for input visitors. */
     void (*optional)(Visitor *v, const char *name, bool *present);
-
-    /* FIXME - needs to be removed */
-    bool (*start_union)(Visitor *v, bool data_present, Error **errp);
-    /* FIXME - needs to be removed */
-    void (*end_union)(Visitor *v, bool data_present, Error **errp);
 };

 /**
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 6e49b51..d7a0110 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -274,16 +274,4 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
  */
 void visit_type_null(Visitor *v, const char *name, Error **errp);

-/**
- * Mark the start of visiting the branches of a union. Return true if
- * @data_present.
- * FIXME: Should not be needed
- */
-bool visit_start_union(Visitor *v, bool data_present, Error **errp);
-/**
- * Mark the end of union branches, after visit_start_union().
- * FIXME: Should not be needed
- */
-void visit_end_union(Visitor *v, bool data_present, Error **errp);
-
 #endif
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index ede1703..1a54864 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -171,31 +171,6 @@ static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
 {
 }

-/* If there's no data present, the dealloc visitor has nothing to free.
- * Thus, indicate to visitor code that the subsequent union fields can
- * be skipped. This is not an error condition, since the cleanup of the
- * rest of an object can continue unhindered, so leave errp unset in
- * these cases.
- *
- * NOTE: In cases where we're attempting to deallocate an object that
- * may have missing fields, the field indicating the union type may
- * be missing. In such a case, it's possible we don't have enough
- * information to differentiate data_present == false from a case where
- * data *is* present but happens to be a scalar with a value of 0.
- * This is okay, since in the case of the dealloc visitor there's no
- * work that needs to done in either situation.
- *
- * The current inability in QAPI code to more thoroughly verify a union
- * type in such cases will likely need to be addressed if we wish to
- * implement this interface for other types of visitors in the future,
- * however.
- */
-static bool qapi_dealloc_start_union(Visitor *v, bool data_present,
-                                     Error **errp)
-{
-    return data_present;
-}
-
 Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
 {
     return &v->visitor;
@@ -227,7 +202,6 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_number = qapi_dealloc_type_number;
     v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_null = qapi_dealloc_type_null;
-    v->visitor.start_union = qapi_dealloc_start_union;

     QTAILQ_INIT(&v->stack);

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 399256b..3360cdf 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -76,21 +76,6 @@ void visit_end_list(Visitor *v)
     v->end_list(v);
 }

-bool visit_start_union(Visitor *v, bool data_present, Error **errp)
-{
-    if (v->start_union) {
-        return v->start_union(v, data_present, errp);
-    }
-    return true;
-}
-
-void visit_end_union(Visitor *v, bool data_present, Error **errp)
-{
-    if (v->end_union) {
-        v->end_union(v, data_present, errp);
-    }
-}
-
 bool visit_optional(Visitor *v, const char *name, bool *present)
 {
     if (v->optional) {
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index feef17f..7a44c13 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -60,10 +60,16 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
     Error *err = NULL;

     visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
-    if (!err) {
-        visit_type_%(c_type)s_fields(v, obj, errp);
-        visit_end_implicit_struct(v);
+    if (err) {
+        goto out;
     }
+    if (!*obj) {
+        goto out_obj;
+    }
+    visit_type_%(c_type)s_fields(v, obj, &err);
+out_obj:
+    visit_end_implicit_struct(v);
+out:
     error_propagate(errp, err);
 }
 ''',
@@ -254,9 +260,6 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error

     if variants:
         ret += mcgen('''
-    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
-        goto out_obj;
-    }
     switch ((*obj)->%(c_name)s) {
 ''',
                      c_name=c_name(variants.tag_member.name))
@@ -293,16 +296,6 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error

     ret += mcgen('''
 out_obj:
-''')
-    if variants:
-        ret += mcgen('''
-    error_propagate(errp, err);
-    err = NULL;
-    if (*obj) {
-        visit_end_union(v, !!(*obj)->u.data, &err);
-    }
-''')
-    ret += mcgen('''
     error_propagate(errp, err);
     err = NULL;
     visit_end_struct(v, &err);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (31 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-27 17:20   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list() Eric Blake
                   ` (4 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, Michael S. Tsirkin, Alexander Graf, armbru,
	open list:sPAPR, Paolo Bonzini, marcandre.lureau,
	Luiz Capitulino, Andreas Färber, David Gibson

As mentioned in previous patches, we want to call visit_end_struct()
functions unconditionally, so that visitors can release resources
tied up since the matching visit_start_struct() without also having
to worry about error priority if more than one error occurs.

Even though error_propagate() can be safely used to ignore a second
error during cleanup caused by a first error, it is simpler if the
cleanup cannot set an error, and we instead split the task of
checking that an input visitor has no unvisited input as a new
function visit_check_struct(), called only if all prior steps are
successful.

Generated code has diffs resembling:

|@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v,
|         goto out_obj;
|     }
|     visit_type_ACPIOSTInfo_fields(v, obj, &err);
|+    if (err) {
|+        goto out_obj;
|+    }
|+    visit_check_struct(v, &err);
| out_obj:
|-    error_propagate(errp, err);
|-    err = NULL;
|-    visit_end_struct(v, &err);
|+    visit_end_struct(v);
| out:

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

---
v9: rebase to earlier changes, drop Marc-Andre's R-b
v8: rebase to 'name' motion
v7: rebase to earlier changes
v6: new patch, revised version of RFC based on discussion of v5 7/46
---
 hmp.c                          |  7 ++++---
 hw/ppc/spapr_drc.c             |  3 ++-
 hw/virtio/virtio-balloon.c     | 12 ++++++------
 include/qapi/visitor-impl.h    |  4 +++-
 include/qapi/visitor.h         | 13 ++++++++++---
 qapi/opts-visitor.c            | 17 +++++++++++++++--
 qapi/qapi-dealloc-visitor.c    |  2 +-
 qapi/qapi-visit-core.c         | 11 +++++++++--
 qapi/qmp-input-visitor.c       | 34 +++++++++++++++++++---------------
 qapi/qmp-output-visitor.c      |  2 +-
 qom/object.c                   |  5 ++---
 scripts/qapi-event.py          |  3 ++-
 scripts/qapi-visit.py          |  9 ++++-----
 tests/test-qmp-input-visitor.c |  3 ++-
 vl.c                           | 11 +++++------
 15 files changed, 85 insertions(+), 51 deletions(-)

diff --git a/hmp.c b/hmp.c
index a4b74df..245617d 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1687,13 +1687,14 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
     }

     object_add(type, id, pdict, v, &err);
-
-out_end:
-    visit_end_struct(v, &err_end);
+    visit_check_struct(v, &err_end);
     if (!err && err_end) {
         qmp_object_del(id, NULL);
     }
     error_propagate(&err, err_end);
+
+out_end:
+    visit_end_struct(v);
 out_clean:
     opts_visitor_cleanup(ov);

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 831ce23..3b27caa 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -287,11 +287,12 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
         case FDT_END_NODE:
             /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
             g_assert(fdt_depth > 0);
-            visit_end_struct(v, &err);
+            visit_check_struct(v, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
             }
+            visit_end_struct(v);
             fdt_depth--;
             break;
         case FDT_PROP: {
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 24ecd87..cefd43f 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -136,15 +136,15 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
             goto out_nested;
         }
     }
+    visit_check_struct(v, &err);
 out_nested:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, &err);
+    visit_end_struct(v);

+    if (!err) {
+        visit_check_struct(v, &err);
+    }
 out_end:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, &err);
+    visit_end_struct(v);
 out:
     error_propagate(errp, err);
 }
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 3b68b7b..248b1e5 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -28,8 +28,10 @@ struct Visitor
      * currently visit structs). */
     void (*start_struct)(Visitor *v, const char *name, void **obj,
                          size_t size, Error **errp);
+    /* May be NULL; most useful for input visitors. */
+    void (*check_struct)(Visitor *v, Error **errp);
     /* Must be provided if start_struct is present. */
-    void (*end_struct)(Visitor *v, Error **errp);
+    void (*end_struct)(Visitor *v);

     /* May be NULL; most useful for input visitors. */
     void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index d7a0110..e5dcde4 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -68,12 +68,19 @@ typedef struct GenericList
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp);
 /**
+ * Prepare for completing a struct visit.
+ * Should be called prior to visit_end_struct() if all other intermediate
+ * visit steps were successful, to allow the caller one last chance to
+ * report errors such as remaining data that was not consumed by the visit.
+ */
+void visit_check_struct(Visitor *v, Error **errp);
+/**
  * Complete a struct visit started earlier.
  * Must be called after any successful use of visit_start_struct(),
  * even if intermediate processing was skipped due to errors, to allow
  * the backend to release any resources.
  */
-void visit_end_struct(Visitor *v, Error **errp);
+void visit_end_struct(Visitor *v);

 /**
  * Prepare to visit an implicit struct.
@@ -96,8 +103,8 @@ void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
  * Must be called after any successful use of visit_start_implicit_struct(),
  * even if intermediate processing was skipped due to errors, to allow
  * the backend to release any resources.  Unlike visit_end_struct(), this
- * does not need to check for errors (detection of unused keys is only
- * possible for the overall struct, not a subset).
+ * does not need a counterpart function to check for errors (detection of
+ * unused keys is only possible for the overall struct, not a subset).
  */
 void visit_end_implicit_struct(Visitor *v);

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index df312e6..b469573 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -157,13 +157,13 @@ opts_start_struct(Visitor *v, const char *name, void **obj,


 static void
-opts_end_struct(Visitor *v, Error **errp)
+opts_check_struct(Visitor *v, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     GHashTableIter iter;
     GQueue *any;

-    if (--ov->depth > 0) {
+    if (ov->depth > 0) {
         return;
     }

@@ -175,6 +175,18 @@ opts_end_struct(Visitor *v, Error **errp)
         first = g_queue_peek_head(any);
         error_setg(errp, QERR_INVALID_PARAMETER, first->name);
     }
+}
+
+
+static void
+opts_end_struct(Visitor *v)
+{
+    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
+
+    if (--ov->depth > 0) {
+        return;
+    }
+
     g_hash_table_destroy(ov->unprocessed_opts);
     ov->unprocessed_opts = NULL;
     if (ov->fake_id_opt) {
@@ -506,6 +518,7 @@ opts_visitor_new(const QemuOpts *opts)
     ov = g_malloc0(sizeof *ov);

     ov->visitor.start_struct = &opts_start_struct;
+    ov->visitor.check_struct = &opts_check_struct;
     ov->visitor.end_struct   = &opts_end_struct;

     ov->visitor.start_list = &opts_start_list;
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 1a54864..a89e6d1 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -65,7 +65,7 @@ static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
     qapi_dealloc_push(qov, obj);
 }

-static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
+static void qapi_dealloc_end_struct(Visitor *v)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     void **obj = qapi_dealloc_pop(qov);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 3360cdf..9506a02 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -36,9 +36,16 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
     v->start_struct(v, name, obj, size, errp);
 }

-void visit_end_struct(Visitor *v, Error **errp)
+void visit_check_struct(Visitor *v, Error **errp)
 {
-    v->end_struct(v, errp);
+    if (v->check_struct) {
+        v->check_struct(v, errp);
+    }
+}
+
+void visit_end_struct(Visitor *v)
+{
+    v->end_struct(v);
 }

 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 91ef945..f256d9e 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -89,8 +89,10 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
 }


-static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
+static void qmp_input_check_struct(Visitor *v, Error **errp)
 {
+    QmpInputVisitor *qiv = to_qiv(v);
+
     assert(qiv->nb_stack > 0);

     if (qiv->strict) {
@@ -103,6 +105,19 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
             if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
                 error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
             }
+        }
+    }
+}
+
+static void qmp_input_pop(Visitor *v)
+{
+    QmpInputVisitor *qiv = to_qiv(v);
+
+    assert(qiv->nb_stack > 0);
+
+    if (qiv->strict) {
+        GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
+        if (top_ht) {
             g_hash_table_unref(top_ht);
         }
     }
@@ -134,12 +149,6 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
     }
 }

-static void qmp_input_end_struct(Visitor *v, Error **errp)
-{
-    QmpInputVisitor *qiv = to_qiv(v);
-
-    qmp_input_pop(qiv, errp);
-}

 static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
                                             size_t size, Error **errp)
@@ -193,12 +202,6 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
     return entry;
 }

-static void qmp_input_end_list(Visitor *v)
-{
-    QmpInputVisitor *qiv = to_qiv(v);
-
-    qmp_input_pop(qiv, &error_abort);
-}

 static void qmp_input_get_next_type(Visitor *v, const char *name, QType *type,
                                     bool promote_int, Error **errp)
@@ -355,11 +358,12 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v = g_malloc0(sizeof(*v));

     v->visitor.start_struct = qmp_input_start_struct;
-    v->visitor.end_struct = qmp_input_end_struct;
+    v->visitor.check_struct = qmp_input_check_struct;
+    v->visitor.end_struct = qmp_input_pop;
     v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
     v->visitor.start_list = qmp_input_start_list;
     v->visitor.next_list = qmp_input_next_list;
-    v->visitor.end_list = qmp_input_end_list;
+    v->visitor.end_list = qmp_input_pop;
     v->visitor.type_enum = input_type_enum;
     v->visitor.type_int64 = qmp_input_type_int64;
     v->visitor.type_uint64 = qmp_input_type_uint64;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 2eb200d..5376948 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -116,7 +116,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
     qmp_output_push(qov, dict);
 }

-static void qmp_output_end_struct(Visitor *v, Error **errp)
+static void qmp_output_end_struct(Visitor *v)
 {
     QmpOutputVisitor *qov = to_qov(v);
     qmp_output_pop(qov, QTYPE_QDICT);
diff --git a/qom/object.c b/qom/object.c
index e5b0566..4644cbd 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -2034,10 +2034,9 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name,
     if (err) {
         goto out_end;
     }
+    visit_check_struct(v, &err);
 out_end:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, errp);
+    visit_end_struct(v);
 out:
     error_propagate(errp, err);

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index cc55de7..dea45c4 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -71,8 +71,9 @@ def gen_event_send(name, arg_type):
         ret += gen_visit_fields(arg_type.members, need_cast=True,
                                 label='out_obj')
         ret += mcgen('''
+    visit_check_struct(v, &err);
 out_obj:
-    visit_end_struct(v, &err);
+    visit_end_struct(v);
     if (err) {
         goto out;
     }
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 7a44c13..8039b97 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -255,8 +255,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     visit_type_%(c_name)s_fields(v, %(cast)sobj, &err);
 ''',
                      c_name=type_name, cast=cast)
-        if variants:
-            ret += gen_err_check(label='out_obj')
+        ret += gen_err_check(label='out_obj')

     if variants:
         ret += mcgen('''
@@ -293,12 +292,12 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         abort();
     }
 ''')
+        ret += gen_err_check(label='out_obj')

     ret += mcgen('''
+    visit_check_struct(v, &err);
 out_obj:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, &err);
+    visit_end_struct(v);
 out:
     error_propagate(errp, err);
 }
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 6489e4a..cd5e765 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -290,7 +290,8 @@ static void test_visitor_in_null(TestInputVisitorData *data,
     v = visitor_input_test_init(data, "{ 'a': null }");
     visit_start_struct(v, NULL, NULL, 0, &error_abort);
     visit_type_null(v, "a", &error_abort);
-    visit_end_struct(v, &error_abort);
+    visit_check_struct(v, &error_abort);
+    visit_end_struct(v);

     /* Check that qnull reference counting is sane:
      * 1 for global use, 1 for our qnull() use, and 1 still owned by 'v'
diff --git a/vl.c b/vl.c
index 22062b5..00c92b8 100644
--- a/vl.c
+++ b/vl.c
@@ -2842,11 +2842,10 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     qdict_del(pdict, "qom-type");
     visit_type_str(v, "qom-type", &type, &err);
     if (err) {
-        goto out;
+        goto out_end;
     }
     if (!type_predicate(type)) {
-        visit_end_struct(v, NULL);
-        goto out;
+        goto out_end;
     }

     qdict_del(pdict, "id");
@@ -2856,14 +2855,14 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     }

     object_add(type, id, pdict, v, &err);
-
-out_end:
-    visit_end_struct(v, &err_end);
+    visit_check_struct(v, &err_end);
     if (!err && err_end) {
         qmp_object_del(id, NULL);
     }
     error_propagate(&err, err_end);

+out_end:
+    visit_end_struct(v);
 out:
     opts_visitor_cleanup(ov);

-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list()
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (32 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-28 13:37   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
                   ` (3 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, Alexander Graf, open list:sPAPR,
	marcandre.lureau, David Gibson

We have two uses of list visits in the entire code base: one in
spapr_drc (which completely avoids visit_next_list(), feeding in
integers from a different source than uint8List), and one in
qapi-visit.py (that is, all other list visitors are generated
in qapi-visit.c, and share the same paradigm based on a qapi
FooList type).  What's more, the semantics of the list visit are
somewhat baroque, with the following pseudocode when FooList is
used:

start()
prev = head
while (cur = next(prev)) {
    visit(cur)
    prev = &cur
}

Note that these semantics (advance before visit) requires that
the first call to next() return the list head, while all other
calls return the next element of the list; that is, every visitor
implementation is required to track extra state to decide whether
to return the input as-is, or to advance.  It also requires an
argument of 'GenericList **' to next(), solely because the first
iteration might need to modify the caller's GenericList head, so
that all other calls have to do a layer of dereferencing.

We can greatly simplify things by hoisting the special case
into the start() routine, and flipping the order in the loop
to visit before advance:

start(head)
element = *head
while (element) {
    visit(element)
    element = next(element)
}

With the simpler semantics, visitors have less state to track,
the argument to next() is reduced to 'GenericList *', and it
also becomes obvious whether an input visitor is allocating a
FooList during visit_start_list() (rather than the old way of
not knowing if an allocation happened until the first
visit_next_list()).

The signature of visit_start_list() is chosen to match
visit_start_struct(), with the new parameter after 'name'.

The spapr_drc case requires that visit_start_list() has to pay
attention to whether visit_next_list() will even be used to
visit a FooList qapi struct; this is done by passing NULL for
list, similarly to how NULL is passed to visit_start_struct()
when a qapi type is not used in those visits.  It was easy to
provide these semantics for qmp-output and dealloc visitors,
and a bit harder for qmp-input (it required hoisting the
advance of the current qlist entry out of qmp_input_next_list()
into qmp_input_get_object()).  But it turned out that the
string and opts visitors munge enough state during
visit_next_list() to make those conversions simpler if they
require a GenericList visit for now; an assertion will remind
us to adjust things if we need the semantics in the future.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

---
v9: no change
v8: consistent parameter order, fix qmp_input_get_next_type() to not
skip list entries
v7: new patch
---
 hw/ppc/spapr_drc.c           |  2 +-
 include/qapi/visitor-impl.h  |  5 ++--
 include/qapi/visitor.h       | 45 +++++++++++++++++--------------
 qapi/opts-visitor.c          | 32 +++++++++-------------
 qapi/qapi-dealloc-visitor.c  | 29 +++++---------------
 qapi/qapi-visit-core.c       |  7 ++---
 qapi/qmp-input-visitor.c     | 64 +++++++++++++++++++++-----------------------
 qapi/qmp-output-visitor.c    | 21 +++------------
 qapi/string-input-visitor.c  | 34 +++++++++++------------
 qapi/string-output-visitor.c | 36 ++++++++-----------------
 scripts/qapi-visit.py        | 21 ++++++++-------
 11 files changed, 126 insertions(+), 170 deletions(-)

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 3b27caa..41f2da0 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -299,7 +299,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
             int i;
             prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
             name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
-            visit_start_list(v, name, &err);
+            visit_start_list(v, name, NULL, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 248b1e5..acbe7d6 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -40,9 +40,10 @@ struct Visitor
     void (*end_implicit_struct)(Visitor *v);

     /* Must be set */
-    void (*start_list)(Visitor *v, const char *name, Error **errp);
+    void (*start_list)(Visitor *v, const char *name, GenericList **list,
+                       Error **errp);
     /* Must be set */
-    GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
+    GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);
     /* Must be set */
     void (*end_list)(Visitor *v);

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index e5dcde4..4638863 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -1,6 +1,7 @@
 /*
  * Core Definitions for QAPI Visitor Classes
  *
+ * Copyright (C) 2015 Red Hat, Inc.
  * Copyright IBM, Corp. 2011
  *
  * Authors:
@@ -111,32 +112,36 @@ void visit_end_implicit_struct(Visitor *v);
 /**
  * Prepare to visit a list tied to an object key @name.
  * @name will be NULL if this is visited as part of another list.
- * After calling this, the elements must be collected until
- * visit_next_list() returns NULL, then visit_end_list() must be
- * used to complete the visit.
- */
-void visit_start_list(Visitor *v, const char *name, Error **errp);
-/**
- * Iterate over a GenericList during a list visit.
- * @list must not be NULL; on the first call, @list contains the
- * address of the list head, and on subsequent calls *@list must be
- * the previously returned value.  Must be called in a loop until a
- * NULL return or error occurs; for each non-NULL return, the caller
- * must then call the appropriate visit_type_*() for the element type
- * of the list, with that function's name parameter set to NULL.
+ * Input visitors malloc a qapi List struct into *@list, or set it to
+ * NULL if there are no elements in the list; and output visitors
+ * expect *@list to point to the start of the list, if any.  On
+ * return, if *@list is non-NULL, the caller should enter a loop
+ * visiting the current element, then using visit_next_list() to
+ * advance to the next element, until that returns NULL; then
+ * visit_end_list() must be used to complete the visit.
  *
- * Note that for some visitors (qapi-dealloc and qmp-output), when a
- * qapi GenericList linked list is not being used (comparable to when
- * a NULL obj is used for visit_start_struct()), it is acceptable to
- * bypass the use of visit_next_list() and just directly call the
- * appropriate visit_type_*() for each element in between the
- * visit_start_list() and visit_end_list() calls.
+ * If supported by a visitor, @list can be NULL to indicate that there
+ * is no qapi List struct, and that the upcoming visit calls are
+ * parsing input to or creating output from some other representation;
+ * in this case, visit_next_list() will not be needed, but
+ * visit_end_list() is still mandatory.
  *
  * FIXME: For input visitors, *@list can be assigned here even if
  * later visits will fail; this can lead to memory leaks if clients
  * aren't careful.
  */
-GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
+void visit_start_list(Visitor *v, const char *name, GenericList **list,
+                      Error **errp);
+/**
+ * Iterate over a GenericList during a list visit.
+ * Before calling this function, the caller should use the appropriate
+ * visit_type_FOO() for the current list element at @element->value, and
+ * check for errors. @element must not be NULL; on the first iteration,
+ * it should be the value in *list after visit_start_list(); on other
+ * calls it should be the previous return value.  This function
+ * returns NULL once there are no further list elements.
+ */
+GenericList *visit_next_list(Visitor *v, GenericList *element, Error **errp);
 /**
  * Complete the list started earlier.
  * Must be called after any successful use of visit_start_list(),
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index b469573..c5a7396 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -21,9 +21,8 @@
 enum ListMode
 {
     LM_NONE,             /* not traversing a list of repeated options */
-    LM_STARTED,          /* opts_start_list() succeeded */

-    LM_IN_PROGRESS,      /* opts_next_list() has been called.
+    LM_IN_PROGRESS,      /* opts_next_list() ready to be called.
                           *
                           * Generating the next list link will consume the most
                           * recently parsed QemuOpt instance of the repeated
@@ -212,35 +211,32 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)


 static void
-opts_start_list(Visitor *v, const char *name, Error **errp)
+opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);

     /* we can't traverse a list in a list */
     assert(ov->list_mode == LM_NONE);
+    /* we don't support visits without GenericList, yet */
+    assert(list);
     ov->repeated_opts = lookup_distinct(ov, name, errp);
-    if (ov->repeated_opts != NULL) {
-        ov->list_mode = LM_STARTED;
+    if (ov->repeated_opts) {
+        ov->list_mode = LM_IN_PROGRESS;
+        *list = g_new0(GenericList, 1);
+    } else {
+        *list = NULL;
     }
 }


 static GenericList *
-opts_next_list(Visitor *v, GenericList **list, Error **errp)
+opts_next_list(Visitor *v, GenericList *list, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
-    GenericList **link;

     switch (ov->list_mode) {
-    case LM_STARTED:
-        ov->list_mode = LM_IN_PROGRESS;
-        link = list;
-        break;
-
     case LM_SIGNED_INTERVAL:
     case LM_UNSIGNED_INTERVAL:
-        link = &(*list)->next;
-
         if (ov->list_mode == LM_SIGNED_INTERVAL) {
             if (ov->range_next.s < ov->range_limit.s) {
                 ++ov->range_next.s;
@@ -261,7 +257,6 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
             g_hash_table_remove(ov->unprocessed_opts, opt->name);
             return NULL;
         }
-        link = &(*list)->next;
         break;
     }

@@ -269,8 +264,8 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
         abort();
     }

-    *link = g_malloc0(sizeof **link);
-    return *link;
+    list->next = g_new0(GenericList, 1);
+    return list->next;
 }


@@ -279,8 +274,7 @@ opts_end_list(Visitor *v)
 {
     OptsVisitor *ov = to_ov(v);

-    assert(ov->list_mode == LM_STARTED ||
-           ov->list_mode == LM_IN_PROGRESS ||
+    assert(ov->list_mode == LM_IN_PROGRESS ||
            ov->list_mode == LM_SIGNED_INTERVAL ||
            ov->list_mode == LM_UNSIGNED_INTERVAL);
     ov->repeated_opts = NULL;
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index a89e6d1..839049e 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -20,7 +20,6 @@
 typedef struct StackEntry
 {
     void *value;
-    bool is_list_head;
     QTAILQ_ENTRY(StackEntry) node;
 } StackEntry;

@@ -41,10 +40,6 @@ static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)

     e->value = value;

-    /* see if we're just pushing a list head tracker */
-    if (value == NULL) {
-        e->is_list_head = true;
-    }
     QTAILQ_INSERT_HEAD(&qov->stack, e, node);
 }

@@ -92,31 +87,19 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v)
     }
 }

-static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp)
+static void qapi_dealloc_start_list(Visitor *v, const char *name,
+                                    GenericList **list, Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, NULL);
 }

-static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
+static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
                                            Error **errp)
 {
-    GenericList *list = *listp;
-    QapiDeallocVisitor *qov = to_qov(v);
-    StackEntry *e = QTAILQ_FIRST(&qov->stack);
-
-    if (e && e->is_list_head) {
-        e->is_list_head = false;
-        return list;
-    }
-
-    if (list) {
-        list = list->next;
-        g_free(*listp);
-        return list;
-    }
-
-    return NULL;
+    GenericList *next = list->next;
+    g_free(list);
+    return next;
 }

 static void qapi_dealloc_end_list(Visitor *v)
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 9506a02..f391a70 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -67,12 +67,13 @@ void visit_end_implicit_struct(Visitor *v)
     }
 }

-void visit_start_list(Visitor *v, const char *name, Error **errp)
+void visit_start_list(Visitor *v, const char *name, GenericList **list,
+                      Error **errp)
 {
-    v->start_list(v, name, errp);
+    v->start_list(v, name, list, errp);
 }

-GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
+GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
 {
     assert(list);
     return v->next_list(v, list, errp);
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index f256d9e..82f9333 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -44,16 +44,20 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
                                      const char *name,
                                      bool consume)
 {
-    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
+    StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+    QObject *qobj = so->obj;

     if (qobj) {
         if (name && qobject_type(qobj) == QTYPE_QDICT) {
-            if (qiv->stack[qiv->nb_stack - 1].h && consume) {
-                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
+            if (so->h && consume) {
+                g_hash_table_remove(so->h, name);
+            }
+            qobj = qdict_get(qobject_to_qdict(qobj), name);
+        } else if (so->entry) {
+            qobj = qlist_entry_obj(so->entry);
+            if (consume) {
+                so->entry = qlist_next(so->entry);
             }
-            return qdict_get(qobject_to_qdict(qobj), name);
-        } else if (qiv->stack[qiv->nb_stack - 1].entry) {
-            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
         }
     }

@@ -66,7 +70,8 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque)
     g_hash_table_insert(h, (gpointer) key, NULL);
 }

-static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
+static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
+                           const QListEntry *entry, Error **errp)
 {
     GHashTable *h;

@@ -76,7 +81,7 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
     }

     qiv->stack[qiv->nb_stack].obj = obj;
-    qiv->stack[qiv->nb_stack].entry = NULL;
+    qiv->stack[qiv->nb_stack].entry = entry;
     qiv->stack[qiv->nb_stack].h = NULL;

     if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
@@ -138,7 +143,7 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
         return;
     }

-    qmp_input_push(qiv, qobj, &err);
+    qmp_input_push(qiv, qobj, NULL, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -158,10 +163,12 @@ static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
     }
 }

-static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
+static void qmp_input_start_list(Visitor *v, const char *name,
+                                 GenericList **list, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
+    const QListEntry *entry;

     if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
@@ -169,37 +176,28 @@ static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
         return;
     }

-    qmp_input_push(qiv, qobj, errp);
+    entry = qlist_first(qobject_to_qlist(qobj));
+    qmp_input_push(qiv, qobj, entry, errp);
+    if (list) {
+        if (entry) {
+            *list = g_new0(GenericList, 1);
+        } else {
+            *list = NULL;
+        }
+    }
 }

-static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
+static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
                                         Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
-    GenericList *entry;
     StackObject *so = &qiv->stack[qiv->nb_stack - 1];
-    bool first;

-    if (so->entry == NULL) {
-        so->entry = qlist_first(qobject_to_qlist(so->obj));
-        first = true;
-    } else {
-        so->entry = qlist_next(so->entry);
-        first = false;
-    }
-
-    if (so->entry == NULL) {
+    if (!so->entry) {
         return NULL;
     }
-
-    entry = g_malloc0(sizeof(*entry));
-    if (first) {
-        *list = entry;
-    } else {
-        (*list)->next = entry;
-    }
-
-    return entry;
+    list->next = g_new0(GenericList, 1);
+    return list->next;
 }


@@ -375,7 +373,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.optional = qmp_input_optional;
     v->visitor.get_next_type = qmp_input_get_next_type;

-    qmp_input_push(v, obj, NULL);
+    qmp_input_push(v, obj, NULL, NULL);
     qobject_incref(obj);

     return v;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 5376948..913f378 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -21,7 +21,6 @@
 typedef struct QStackEntry
 {
     QObject *value;
-    bool is_list_head;
     QTAILQ_ENTRY(QStackEntry) node;
 } QStackEntry;

@@ -51,9 +50,6 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
     assert(qov->root);
     assert(value);
     e->value = value;
-    if (qobject_type(e->value) == QTYPE_QLIST) {
-        e->is_list_head = true;
-    }
     QTAILQ_INSERT_HEAD(&qov->stack, e, node);
 }

@@ -122,7 +118,8 @@ static void qmp_output_end_struct(Visitor *v)
     qmp_output_pop(qov, QTYPE_QDICT);
 }

-static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
+static void qmp_output_start_list(Visitor *v, const char *name,
+                                  GenericList **listp, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QList *list = qlist_new();
@@ -131,20 +128,10 @@ static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
     qmp_output_push(qov, list);
 }

-static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
+static GenericList *qmp_output_next_list(Visitor *v, GenericList *list,
                                          Error **errp)
 {
-    GenericList *list = *listp;
-    QmpOutputVisitor *qov = to_qov(v);
-    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
-
-    assert(e);
-    if (e->is_list_head) {
-        e->is_list_head = false;
-        return list;
-    }
-
-    return list ? list->next : NULL;
+    return list->next;
 }

 static void qmp_output_end_list(Visitor *v)
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 610c233..582a62a 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -23,8 +23,6 @@ struct StringInputVisitor
 {
     Visitor visitor;

-    bool head;
-
     GList *ranges;
     GList *cur_range;
     int64_t cur;
@@ -123,11 +121,19 @@ error:
 }

 static void
-start_list(Visitor *v, const char *name, Error **errp)
+start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
+    Error *err = NULL;

-    parse_str(siv, errp);
+    /* We don't support visits without a GenericList, yet */
+    assert(list);
+
+    parse_str(siv, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }

     siv->cur_range = g_list_first(siv->ranges);
     if (siv->cur_range) {
@@ -135,14 +141,16 @@ start_list(Visitor *v, const char *name, Error **errp)
         if (r) {
             siv->cur = r->begin;
         }
+        *list = g_new0(GenericList, 1);
+    } else {
+        *list = NULL;
     }
 }

 static GenericList *
-next_list(Visitor *v, GenericList **list, Error **errp)
+next_list(Visitor *v, GenericList *list, Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
-    GenericList **link;
     Range *r;

     if (!siv->ranges || !siv->cur_range) {
@@ -166,22 +174,13 @@ next_list(Visitor *v, GenericList **list, Error **errp)
         siv->cur = r->begin;
     }

-    if (siv->head) {
-        link = list;
-        siv->head = false;
-    } else {
-        link = &(*list)->next;
-    }
-
-    *link = g_malloc0(sizeof **link);
-    return *link;
+    list->next = g_new0(GenericList, 1);
+    return list->next;
 }

 static void
 end_list(Visitor *v)
 {
-    StringInputVisitor *siv = to_siv(v);
-    siv->head = true;
 }

 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
@@ -361,6 +360,5 @@ StringInputVisitor *string_input_visitor_new(const char *str)
     v->visitor.optional = parse_optional;

     v->string = str;
-    v->head = true;
     return v;
 }
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index fd917a4..7209d80 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -57,7 +57,6 @@ struct StringOutputVisitor
     Visitor visitor;
     bool human;
     GString *string;
-    bool head;
     ListMode list_mode;
     union {
         int64_t s;
@@ -265,40 +264,29 @@ static void print_type_number(Visitor *v, const char *name, double *obj,
 }

 static void
-start_list(Visitor *v, const char *name, Error **errp)
+start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);

     /* we can't traverse a list in a list */
     assert(sov->list_mode == LM_NONE);
-    sov->list_mode = LM_STARTED;
-    sov->head = true;
+    /* We don't support visits without a GenericList, yet */
+    assert(list);
+    /* List handling is only needed if there are at least two elements */
+    if (*list && (*list)->next) {
+        sov->list_mode = LM_STARTED;
+    }
 }

 static GenericList *
-next_list(Visitor *v, GenericList **list, Error **errp)
+next_list(Visitor *v, GenericList *list, Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
-    GenericList *ret = NULL;
-    if (*list) {
-        if (sov->head) {
-            ret = *list;
-        } else {
-            ret = (*list)->next;
-        }
+    GenericList *ret = list->next;

-        if (sov->head) {
-            if (ret && ret->next == NULL) {
-                sov->list_mode = LM_NONE;
-            }
-            sov->head = false;
-        } else {
-            if (ret && ret->next == NULL) {
-                sov->list_mode = LM_END;
-            }
-        }
+    if (ret && !ret->next) {
+        sov->list_mode = LM_END;
     }
-
     return ret;
 }

@@ -312,8 +300,6 @@ end_list(Visitor *v)
            sov->list_mode == LM_NONE ||
            sov->list_mode == LM_IN_PROGRESS);
     sov->list_mode = LM_NONE;
-    sov->head = true;
-
 }

 char *string_output_get_string(StringOutputVisitor *sov)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 8039b97..6016734 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -125,20 +125,23 @@ def gen_visit_list(name, element_type):
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
-    GenericList *i, **prev;
+    %(c_name)s *elt;

-    visit_start_list(v, name, &err);
+    visit_start_list(v, name, (GenericList **)obj, &err);
     if (err) {
         goto out;
     }
-
-    for (prev = (GenericList **)obj;
-         !err && (i = visit_next_list(v, prev, &err)) != NULL;
-         prev = &i) {
-        %(c_name)s *native_i = (%(c_name)s *)i;
-        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
+    elt = *obj;
+    while (elt) {
+        visit_type_%(c_elt_type)s(v, NULL, &elt->value, &err);
+        if (err) {
+            break;
+        }
+        elt = (%(c_name)s *)visit_next_list(v, (GenericList *)elt, &err);
+        if (err) {
+            break;
+        }
     }
-
     visit_end_list(v);
 out:
     error_propagate(errp, err);
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (33 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list() Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-28 15:24   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types Eric Blake
                   ` (2 subsequent siblings)
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Returning a partial object on error is an invitation for a careless
caller to leak memory.  As no one outside the testsuite was actually
relying on these semantics, it is cleaner to just document and
guarantee that ALL pointer-based visit_type_FOO() functions always
leave a safe value in *obj during an input visitor (either the new
object on success, or NULL if an error is encountered).

Since input visitors have blind assignment semantics, we have to
track the result of whether an assignment is made all the way down
to each visitor callback implementation, to avoid making decisions
based on potentially uninitialized storage.

Note that we still leave *obj unchanged after a scalar-based
visit_type_FOO(); I did not feel like auditing all uses of
visit_type_Enum() to see if the callers would tolerate a specific
sentinel value (not to mention having to decide whether it would
be better to use 0 or ENUM__MAX as that sentinel).

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

---
v9: fix bug in use of errp
v8: rebase to earlier changes
v7: rebase to earlier changes, enhance commit message, also fix
visit_type_str() and visit_type_any()
v6: rebase on top of earlier doc and formatting improvements, mention
that *obj can be uninitialized on entry to an input visitor, rework
semantics to keep valgrind happy on uninitialized input, break some
long lines
---
 include/qapi/visitor-impl.h    |  6 ++---
 include/qapi/visitor.h         | 53 ++++++++++++++++++++++++++++--------------
 qapi/opts-visitor.c            | 11 ++++++---
 qapi/qapi-dealloc-visitor.c    |  9 ++++---
 qapi/qapi-visit-core.c         | 45 ++++++++++++++++++++++++++++-------
 qapi/qmp-input-visitor.c       | 18 +++++++++-----
 qapi/qmp-output-visitor.c      |  6 +++--
 qapi/string-input-visitor.c    |  6 +++--
 qapi/string-output-visitor.c   |  3 ++-
 scripts/qapi-visit.py          | 40 +++++++++++++++++++++++--------
 tests/test-qmp-commands.c      | 13 +++++------
 tests/test-qmp-input-strict.c  | 19 +++++++--------
 tests/test-qmp-input-visitor.c | 10 ++------
 13 files changed, 157 insertions(+), 82 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index acbe7d6..8df4ba1 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -26,7 +26,7 @@ struct Visitor
 {
     /* Must be provided to visit structs (the string visitors do not
      * currently visit structs). */
-    void (*start_struct)(Visitor *v, const char *name, void **obj,
+    bool (*start_struct)(Visitor *v, const char *name, void **obj,
                          size_t size, Error **errp);
     /* May be NULL; most useful for input visitors. */
     void (*check_struct)(Visitor *v, Error **errp);
@@ -34,13 +34,13 @@ struct Visitor
     void (*end_struct)(Visitor *v);

     /* May be NULL; most useful for input visitors. */
-    void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
+    bool (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
                                   Error **errp);
     /* May be NULL */
     void (*end_implicit_struct)(Visitor *v);

     /* Must be set */
-    void (*start_list)(Visitor *v, const char *name, GenericList **list,
+    bool (*start_list)(Visitor *v, const char *name, GenericList **list,
                        Error **errp);
     /* Must be set */
     GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 4638863..4eae633 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -31,6 +31,27 @@
  * visitor-impl.h.
  */

+/* All qapi types have a corresponding function with a signature
+ * roughly compatible with this:
+ *
+ * void visit_type_FOO(Visitor *v, void *obj, const char *name, Error **errp);
+ *
+ * where *@obj is itself a pointer or a scalar.  The visit functions for
+ * built-in types are declared here, while the functions for qapi-defined
+ * struct, union, enum, and list types are generated (see qapi-visit.h).
+ * Input visitors may receive an uninitialized *@obj, and guarantee a
+ * safe value is assigned (a new object on success, or NULL on failure).
+ * Output visitors expect *@obj to be properly initialized on entry.
+ *
+ * Additionally, all qapi structs have a generated function compatible
+ * with this:
+ *
+ * void qapi_free_FOO(void *obj);
+ *
+ * which behaves like free(), even if @obj is NULL or was only partially
+ * allocated before encountering an error.
+ */
+
 /* This struct is layout-compatible with all other *List structs
  * created by the qapi generator. */
 typedef struct GenericList
@@ -62,11 +83,12 @@ typedef struct GenericList
  * Set *@errp on failure; for example, if the input stream does not
  * have a member @name or if the member is not an object.
  *
- * FIXME: For input visitors, *@obj can be assigned here even if later
- * visits will fail; this can lead to memory leaks if clients aren't
- * careful.
+ * Returns true if *@obj was allocated; if that happens, and an error
+ * occurs any time before the matching visit_end_struct(), then the
+ * caller (usually a visit_type_FOO() function) knows to undo the
+ * allocation before returning control further.
  */
-void visit_start_struct(Visitor *v, const char *name, void **obj,
+bool visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp);
 /**
  * Prepare for completing a struct visit.
@@ -85,19 +107,15 @@ void visit_end_struct(Visitor *v);

 /**
  * Prepare to visit an implicit struct.
- * Similar to visit_start_struct(), except that an implicit struct
- * represents a subset of keys that are present at the same nesting level
- * of a common object but as a separate qapi C struct, rather than a new
- * object at a deeper nesting level.
+ * Similar to visit_start_struct(), including return semantics, except
+ * that an implicit struct represents a subset of keys that are present
+ * at the same nesting level of a common object but as a separate qapi
+ * C struct, rather than a new object at a deeper nesting level.
  *
  * @obj must not be NULL, since this function is only called when
  * visiting with qapi structs.
- *
- * FIXME: For input visitors, *@obj can be assigned here even if later
- * visits will fail; this can lead to memory leaks if clients aren't
- * careful.
  */
-void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
+bool visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
 /**
  * Complete an implicit struct visit started earlier.
@@ -126,11 +144,12 @@ void visit_end_implicit_struct(Visitor *v);
  * in this case, visit_next_list() will not be needed, but
  * visit_end_list() is still mandatory.
  *
- * FIXME: For input visitors, *@list can be assigned here even if
- * later visits will fail; this can lead to memory leaks if clients
- * aren't careful.
+ * Returns true if *@list was allocated; if that happens, and an error
+ * occurs any time before the matching visit_end_list(), then the
+ * caller (usually a visit_type_FOO() function) knows to undo the
+ * allocation before returning control further.
  */
-void visit_start_list(Visitor *v, const char *name, GenericList **list,
+bool visit_start_list(Visitor *v, const char *name, GenericList **list,
                       Error **errp);
 /**
  * Iterate over a GenericList during a list visit.
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index c5a7396..38d1e68 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -122,18 +122,20 @@ opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
 }


-static void
+static bool
 opts_start_struct(Visitor *v, const char *name, void **obj,
                   size_t size, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
+    bool result = false;

     if (obj) {
         *obj = g_malloc0(size > 0 ? size : 1);
+        result = true;
     }
     if (ov->depth++ > 0) {
-        return;
+        return result;
     }

     ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
@@ -152,6 +154,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
         ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
         opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
     }
+    return result;
 }


@@ -210,7 +213,7 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
 }


-static void
+static bool
 opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
@@ -223,8 +226,10 @@ opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
     if (ov->repeated_opts) {
         ov->list_mode = LM_IN_PROGRESS;
         *list = g_new0(GenericList, 1);
+        return true;
     } else {
         *list = NULL;
+        return false;
     }
 }

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 839049e..0990199 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -53,11 +53,12 @@ static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
     return value;
 }

-static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
+static bool qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
                                       size_t unused, Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, obj);
+    return false;
 }

 static void qapi_dealloc_end_struct(Visitor *v)
@@ -69,13 +70,14 @@ static void qapi_dealloc_end_struct(Visitor *v)
     }
 }

-static void qapi_dealloc_start_implicit_struct(Visitor *v,
+static bool qapi_dealloc_start_implicit_struct(Visitor *v,
                                                void **obj,
                                                size_t size,
                                                Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, obj);
+    return false;
 }

 static void qapi_dealloc_end_implicit_struct(Visitor *v)
@@ -87,11 +89,12 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v)
     }
 }

-static void qapi_dealloc_start_list(Visitor *v, const char *name,
+static bool qapi_dealloc_start_list(Visitor *v, const char *name,
                                     GenericList **list, Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, NULL);
+    return false;
 }

 static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index f391a70..259e0cb 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -2,7 +2,7 @@
  * Core Definitions for QAPI Visitor Classes
  *
  * Copyright IBM, Corp. 2011
- * Copyright (C) 2015 Red Hat, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
  *
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
@@ -26,14 +26,20 @@ static bool visit_is_output(Visitor *v)
     return v->type_enum == output_type_enum;
 }

-void visit_start_struct(Visitor *v, const char *name, void **obj,
+bool visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp)
 {
+    bool result;
+
     assert(obj ? size : !size);
     if (obj && visit_is_output(v)) {
         assert(*obj);
     }
-    v->start_struct(v, name, obj, size, errp);
+    result = v->start_struct(v, name, obj, size, errp);
+    if (result) {
+        assert(obj && *obj);
+    }
+    return result;
 }

 void visit_check_struct(Visitor *v, Error **errp)
@@ -48,16 +54,22 @@ void visit_end_struct(Visitor *v)
     v->end_struct(v);
 }

-void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
+bool visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp)
 {
+    bool result = false;
+
     assert(obj && size);
     if (visit_is_output(v)) {
         assert(*obj);
     }
     if (v->start_implicit_struct) {
-        v->start_implicit_struct(v, obj, size, errp);
+        result = v->start_implicit_struct(v, obj, size, errp);
     }
+    if (result) {
+        assert(*obj);
+    }
+    return result;
 }

 void visit_end_implicit_struct(Visitor *v)
@@ -67,10 +79,14 @@ void visit_end_implicit_struct(Visitor *v)
     }
 }

-void visit_start_list(Visitor *v, const char *name, GenericList **list,
+bool visit_start_list(Visitor *v, const char *name, GenericList **list,
                       Error **errp)
 {
-    v->start_list(v, name, list, errp);
+    bool result = v->start_list(v, name, list, errp);
+    if (result) {
+        assert(list && *list);
+    }
+    return result;
 }

 GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
@@ -229,6 +245,7 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)

 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
+    Error *err = NULL;
     assert(obj);
     /* TODO: Fix callers to not pass NULL when they mean "", so that we
      * can enable:
@@ -236,7 +253,11 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
         assert(*obj);
     }
      */
-    v->type_str(v, name, obj, errp);
+    v->type_str(v, name, obj, &err);
+    if (!visit_is_output(v) && err) {
+        *obj = NULL;
+    }
+    error_propagate(errp, err);
 }

 void visit_type_number(Visitor *v, const char *name, double *obj,
@@ -248,11 +269,17 @@ void visit_type_number(Visitor *v, const char *name, double *obj,

 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 {
+    Error *err = NULL;
+
     assert(obj);
     if (visit_is_output(v)) {
         assert(*obj);
     }
-    v->type_any(v, name, obj, errp);
+    v->type_any(v, name, obj, &err);
+    if (!visit_is_output(v) && err) {
+        *obj = NULL;
+    }
+    error_propagate(errp, err);
 }

 void visit_type_null(Visitor *v, const char *name, Error **errp)
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 82f9333..6b4ad68 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -130,7 +130,7 @@ static void qmp_input_pop(Visitor *v)
     qiv->nb_stack--;
 }

-static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
+static bool qmp_input_start_struct(Visitor *v, const char *name, void **obj,
                                    size_t size, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -140,30 +140,34 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
     if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "QDict");
-        return;
+        return false;
     }

     qmp_input_push(qiv, qobj, NULL, &err);
     if (err) {
         error_propagate(errp, err);
-        return;
+        return false;
     }

     if (obj) {
         *obj = g_malloc0(size);
+        return true;
     }
+    return false;
 }


-static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
+static bool qmp_input_start_implicit_struct(Visitor *v, void **obj,
                                             size_t size, Error **errp)
 {
     if (obj) {
         *obj = g_malloc0(size);
+        return true;
     }
+    return false;
 }

-static void qmp_input_start_list(Visitor *v, const char *name,
+static bool qmp_input_start_list(Visitor *v, const char *name,
                                  GenericList **list, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -173,7 +177,7 @@ static void qmp_input_start_list(Visitor *v, const char *name,
     if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "list");
-        return;
+        return false;
     }

     entry = qlist_first(qobject_to_qlist(qobj));
@@ -181,10 +185,12 @@ static void qmp_input_start_list(Visitor *v, const char *name,
     if (list) {
         if (entry) {
             *list = g_new0(GenericList, 1);
+            return true;
         } else {
             *list = NULL;
         }
     }
+    return false;
 }

 static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 913f378..ce592d2 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -102,7 +102,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
     }
 }

-static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
+static bool qmp_output_start_struct(Visitor *v, const char *name, void **obj,
                                     size_t unused, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
@@ -110,6 +110,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,

     qmp_output_add(qov, name, dict);
     qmp_output_push(qov, dict);
+    return false;
 }

 static void qmp_output_end_struct(Visitor *v)
@@ -118,7 +119,7 @@ static void qmp_output_end_struct(Visitor *v)
     qmp_output_pop(qov, QTYPE_QDICT);
 }

-static void qmp_output_start_list(Visitor *v, const char *name,
+static bool qmp_output_start_list(Visitor *v, const char *name,
                                   GenericList **listp, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
@@ -126,6 +127,7 @@ static void qmp_output_start_list(Visitor *v, const char *name,

     qmp_output_add(qov, name, list);
     qmp_output_push(qov, list);
+    return false;
 }

 static GenericList *qmp_output_next_list(Visitor *v, GenericList *list,
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 582a62a..0e4a07c 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -120,7 +120,7 @@ error:
     siv->ranges = NULL;
 }

-static void
+static bool
 start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
@@ -132,7 +132,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
     parse_str(siv, &err);
     if (err) {
         error_propagate(errp, err);
-        return;
+        return false;
     }

     siv->cur_range = g_list_first(siv->ranges);
@@ -142,8 +142,10 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
             siv->cur = r->begin;
         }
         *list = g_new0(GenericList, 1);
+        return true;
     } else {
         *list = NULL;
+        return false;
     }
 }

diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 7209d80..2666802 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -263,7 +263,7 @@ static void print_type_number(Visitor *v, const char *name, double *obj,
     string_output_set(sov, g_strdup_printf("%f", *obj));
 }

-static void
+static bool
 start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
@@ -276,6 +276,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
     if (*list && (*list)->next) {
         sov->list_mode = LM_STARTED;
     }
+    return false;
 }

 static GenericList *
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 6016734..eebb5f7 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -58,8 +58,10 @@ def gen_visit_implicit_struct(typ):
 static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
 {
     Error *err = NULL;
+    bool allocated;

-    visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
+    allocated = visit_start_implicit_struct(v, (void **)obj,
+                                            sizeof(%(c_type)s), &err);
     if (err) {
         goto out;
     }
@@ -69,6 +71,10 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
     visit_type_%(c_type)s_fields(v, obj, &err);
 out_obj:
     visit_end_implicit_struct(v);
+    if (allocated && err) {
+        g_free(*obj);
+        *obj = NULL;
+    }
 out:
     error_propagate(errp, err);
 }
@@ -116,18 +122,15 @@ out:


 def gen_visit_list(name, element_type):
-    # FIXME: if *obj is NULL on entry, and the first visit_next_list()
-    # assigns to *obj, while a later one fails, we should clean up *obj
-    # rather than leaving it non-NULL. As currently written, the caller must
-    # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
     return mcgen('''

 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
     %(c_name)s *elt;
+    bool allocated;

-    visit_start_list(v, name, (GenericList **)obj, &err);
+    allocated = visit_start_list(v, name, (GenericList **)obj, &err);
     if (err) {
         goto out;
     }
@@ -144,6 +147,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     }
     visit_end_list(v);
 out:
+    if (allocated && err) {
+        qapi_free_%(c_name)s(*obj);
+        *obj = NULL;
+    }
     error_propagate(errp, err);
 }
 ''',
@@ -174,8 +181,10 @@ def gen_visit_alternate(name, variants):
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
+    bool allocated;

-    visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
+    allocated = visit_start_implicit_struct(v, (void **)obj,
+                                            sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
@@ -204,11 +213,15 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     }
 out_obj:
     visit_end_implicit_struct(v);
+    if (allocated && err) {
+        qapi_free_%(c_name)s(*obj);
+        *obj = NULL;
+    }
 out:
     error_propagate(errp, err);
 }
 ''',
-                 name=name)
+                 name=name, c_name=c_name(name))

     return ret

@@ -236,8 +249,10 @@ def gen_visit_object(name, base, members, variants):
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
+    bool allocated;

-    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
+    allocated = visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s),
+                                   &err);
     if (err) {
         goto out;
     }
@@ -301,10 +316,15 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     visit_check_struct(v, &err);
 out_obj:
     visit_end_struct(v);
+    if (allocated && err) {
+        qapi_free_%(c_name)s(*obj);
+        *obj = NULL;
+    }
 out:
     error_propagate(errp, err);
 }
-''')
+''',
+                 c_name=c_name(name))

     return ret

diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index bc8085d..d3466a4 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -227,14 +227,13 @@ static void test_dealloc_partial(void)
         QDECREF(ud2_dict);
     }

-    /* verify partial success */
-    assert(ud2 != NULL);
-    assert(ud2->string0 != NULL);
-    assert(strcmp(ud2->string0, text) == 0);
-    assert(ud2->dict1 == NULL);
-
-    /* confirm & release construction error */
+    /* verify that visit_type_XXX() cleans up properly on error */
     error_free_or_abort(&err);
+    assert(!ud2);
+
+    /* Manually create a partial object, leaving ud2->dict1 at NULL */
+    ud2 = g_new0(UserDefTwo, 1);
+    ud2->string0 = g_strdup(text);

     /* tear down partial object */
     qapi_free_UserDefTwo(ud2);
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 775ad39..4db35dd 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -181,10 +181,7 @@ static void test_validate_fail_struct(TestInputVisitorData *data,

     visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
-    if (p) {
-        g_free(p->string);
-    }
-    g_free(p);
+    g_assert(!p);
 }

 static void test_validate_fail_struct_nested(TestInputVisitorData *data,
@@ -198,7 +195,7 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data,

     visit_type_UserDefTwo(v, NULL, &udp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefTwo(udp);
+    g_assert(!udp);
 }

 static void test_validate_fail_list(TestInputVisitorData *data,
@@ -212,7 +209,7 @@ static void test_validate_fail_list(TestInputVisitorData *data,

     visit_type_UserDefOneList(v, NULL, &head, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefOneList(head);
+    g_assert(!head);
 }

 static void test_validate_fail_union_native_list(TestInputVisitorData *data,
@@ -227,7 +224,7 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data,

     visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefNativeListUnion(tmp);
+    g_assert(!tmp);
 }

 static void test_validate_fail_union_flat(TestInputVisitorData *data,
@@ -241,7 +238,7 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data,

     visit_type_UserDefFlatUnion(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefFlatUnion(tmp);
+    g_assert(!tmp);
 }

 static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
@@ -256,13 +253,13 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,

     visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefFlatUnion2(tmp);
+    g_assert(!tmp);
 }

 static void test_validate_fail_alternate(TestInputVisitorData *data,
                                          const void *unused)
 {
-    UserDefAlternate *tmp = NULL;
+    UserDefAlternate *tmp;
     Visitor *v;
     Error *err = NULL;

@@ -270,7 +267,7 @@ static void test_validate_fail_alternate(TestInputVisitorData *data,

     visit_type_UserDefAlternate(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefAlternate(tmp);
+    g_assert(!tmp);
 }

 static void do_test_validate_qmp_introspect(TestInputVisitorData *data,
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index cd5e765..0eef7e0 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -733,18 +733,12 @@ static void test_visitor_in_errors(TestInputVisitorData *data,

     visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
-    /* FIXME - a failed parse should not leave a partially-allocated p
-     * for us to clean up; this could cause callers to leak memory. */
-    g_assert(p->string == NULL);
-
-    g_free(p->string);
-    g_free(p);
+    g_assert(!p);

     v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]");
     visit_type_strList(v, NULL, &q, &err);
     error_free_or_abort(&err);
-    assert(q);
-    qapi_free_strList(q);
+    assert(!q);
 }

 static void test_visitor_in_wrong_type(TestInputVisitorData *data,
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (34 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-28 15:34   ` Markus Armbruster
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes Eric Blake
  2016-01-28 17:56 ` [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, Alexander Graf, open list:sPAPR,
	marcandre.lureau, David Gibson

By sticking the next pointer first, we don't need a union with
64-bit padding for smaller types.  On 32-bit platforms, this
can reduce the size of uint8List from 16 bytes (or 12, depending
on whether 64-bit ints can tolerate 4-byte alignment) down to 8.
It has no effect on 64-bit platforms (where alignment still
dictates a 16-byte struct); but fewer anonymous unions is still
a win in my book.

However, this requires visit_start_list() and visit_next_list()
to gain a size parameter, to know what size element to allocate.

I debated about going one step further, to allow for fewer casts,
by doing:
    typedef GenericList GenericList;
    struct GenericList {
        GenericList *next;
    };
    struct FooList {
        GenericList base;
        Foo value;
    };
so that you convert to 'GenericList *' by '&foolist->base', and
back by 'container_of(generic, GenericList, base)' (as opposed to
the existing '(GenericList *)foolist' and '(FooList *)generic').
But doing that would require hoisting the declaration of
GenericList prior to inclusion of qapi-types.h, rather than its
current spot in visitor.h; it also makes iteration a bit more
verbose through 'foolist->base.next' instead of 'foolist->next'.

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

---
v9: no change
v8: rebase to earlier changes
v7: new patch; probably too invasive to be worth it, especially if
we can't prove that our current size for uint8List is a bottleneck
---
 hw/ppc/spapr_drc.c           |  2 +-
 include/qapi/visitor-impl.h  |  5 +++--
 include/qapi/visitor.h       | 39 +++++++++++++++++++--------------------
 qapi/opts-visitor.c          |  9 +++++----
 qapi/qapi-dealloc-visitor.c  |  5 +++--
 qapi/qapi-visit-core.c       | 14 +++++++++-----
 qapi/qmp-input-visitor.c     |  8 ++++----
 qapi/qmp-output-visitor.c    |  5 +++--
 qapi/string-input-visitor.c  |  9 +++++----
 qapi/string-output-visitor.c |  5 +++--
 scripts/qapi-types.py        |  5 +----
 scripts/qapi-visit.py        |  4 ++--
 12 files changed, 58 insertions(+), 52 deletions(-)

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 41f2da0..0eba901 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -299,7 +299,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
             int i;
             prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
             name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
-            visit_start_list(v, name, NULL, &err);
+            visit_start_list(v, name, NULL, 0, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 8df4ba1..dbbbcdb 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -41,9 +41,10 @@ struct Visitor

     /* Must be set */
     bool (*start_list)(Visitor *v, const char *name, GenericList **list,
-                       Error **errp);
+                       size_t size, Error **errp);
     /* Must be set */
-    GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);
+    GenericList *(*next_list)(Visitor *v, GenericList *element, size_t size,
+                              Error **errp);
     /* Must be set */
     void (*end_list)(Visitor *v);

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 4eae633..c446726 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -56,11 +56,8 @@
  * created by the qapi generator. */
 typedef struct GenericList
 {
-    union {
-        void *value;
-        uint64_t padding;
-    };
     struct GenericList *next;
+    char padding[];
 } GenericList;

 /**
@@ -130,19 +127,19 @@ void visit_end_implicit_struct(Visitor *v);
 /**
  * Prepare to visit a list tied to an object key @name.
  * @name will be NULL if this is visited as part of another list.
- * Input visitors malloc a qapi List struct into *@list, or set it to
- * NULL if there are no elements in the list; and output visitors
- * expect *@list to point to the start of the list, if any.  On
- * return, if *@list is non-NULL, the caller should enter a loop
+ * Input visitors malloc a qapi List struct into *@list of size @size,
+ * or set it to NULL if there are no elements in the list; and output
+ * visitors expect *@list to point to the start of the list, if any.
+ * On return, if *@list is non-NULL, the caller should enter a loop
  * visiting the current element, then using visit_next_list() to
  * advance to the next element, until that returns NULL; then
  * visit_end_list() must be used to complete the visit.
  *
- * If supported by a visitor, @list can be NULL to indicate that there
- * is no qapi List struct, and that the upcoming visit calls are
- * parsing input to or creating output from some other representation;
- * in this case, visit_next_list() will not be needed, but
- * visit_end_list() is still mandatory.
+ * If supported by a visitor, @list can be NULL (and @size 0) to
+ * indicate that there is no qapi List struct, and that the upcoming
+ * visit calls are parsing input to or creating output from some other
+ * representation; in this case, visit_next_list() will not be needed,
+ * but visit_end_list() is still mandatory.
  *
  * Returns true if *@list was allocated; if that happens, and an error
  * occurs any time before the matching visit_end_list(), then the
@@ -150,17 +147,19 @@ void visit_end_implicit_struct(Visitor *v);
  * allocation before returning control further.
  */
 bool visit_start_list(Visitor *v, const char *name, GenericList **list,
-                      Error **errp);
+                      size_t size, Error **errp);
 /**
  * Iterate over a GenericList during a list visit.
  * Before calling this function, the caller should use the appropriate
- * visit_type_FOO() for the current list element at @element->value, and
- * check for errors. @element must not be NULL; on the first iteration,
- * it should be the value in *list after visit_start_list(); on other
- * calls it should be the previous return value.  This function
- * returns NULL once there are no further list elements.
+ * visit_type_FOO() for the current list element at @element->value,
+ * and check for errors. @element must not be NULL; on the first
+ * iteration, it should be the value in *list after
+ * visit_start_list(); on other calls it should be the previous return
+ * value.  @size should be the same as for visit_start_list().  This
+ * function returns NULL once there are no further list elements.
  */
-GenericList *visit_next_list(Visitor *v, GenericList *element, Error **errp);
+GenericList *visit_next_list(Visitor *v, GenericList *element, size_t size,
+                             Error **errp);
 /**
  * Complete the list started earlier.
  * Must be called after any successful use of visit_start_list(),
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 38d1e68..28f9a8a 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -214,7 +214,8 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)


 static bool
-opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
+opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size,
+                Error **errp)
 {
     OptsVisitor *ov = to_ov(v);

@@ -225,7 +226,7 @@ opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
     ov->repeated_opts = lookup_distinct(ov, name, errp);
     if (ov->repeated_opts) {
         ov->list_mode = LM_IN_PROGRESS;
-        *list = g_new0(GenericList, 1);
+        *list = g_malloc0(size);
         return true;
     } else {
         *list = NULL;
@@ -235,7 +236,7 @@ opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)


 static GenericList *
-opts_next_list(Visitor *v, GenericList *list, Error **errp)
+opts_next_list(Visitor *v, GenericList *list, size_t size, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);

@@ -269,7 +270,7 @@ opts_next_list(Visitor *v, GenericList *list, Error **errp)
         abort();
     }

-    list->next = g_new0(GenericList, 1);
+    list->next = g_malloc0(size);
     return list->next;
 }

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 0990199..d498f29 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -90,7 +90,8 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v)
 }

 static bool qapi_dealloc_start_list(Visitor *v, const char *name,
-                                    GenericList **list, Error **errp)
+                                    GenericList **list, size_t size,
+                                    Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, NULL);
@@ -98,7 +99,7 @@ static bool qapi_dealloc_start_list(Visitor *v, const char *name,
 }

 static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
-                                           Error **errp)
+                                           size_t size, Error **errp)
 {
     GenericList *next = list->next;
     g_free(list);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 259e0cb..ed4de71 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -80,19 +80,23 @@ void visit_end_implicit_struct(Visitor *v)
 }

 bool visit_start_list(Visitor *v, const char *name, GenericList **list,
-                      Error **errp)
+                      size_t size, Error **errp)
 {
-    bool result = v->start_list(v, name, list, errp);
+    bool result;
+
+    assert(list ? size : !size);
+    result = v->start_list(v, name, list, size, errp);
     if (result) {
         assert(list && *list);
     }
     return result;
 }

-GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
+GenericList *visit_next_list(Visitor *v, GenericList *list, size_t size,
+                             Error **errp)
 {
-    assert(list);
-    return v->next_list(v, list, errp);
+    assert(list && size);
+    return v->next_list(v, list, size, errp);
 }

 void visit_end_list(Visitor *v)
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 6b4ad68..16c18b3 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -168,7 +168,7 @@ static bool qmp_input_start_implicit_struct(Visitor *v, void **obj,
 }

 static bool qmp_input_start_list(Visitor *v, const char *name,
-                                 GenericList **list, Error **errp)
+                                 GenericList **list, size_t size, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
@@ -184,7 +184,7 @@ static bool qmp_input_start_list(Visitor *v, const char *name,
     qmp_input_push(qiv, qobj, entry, errp);
     if (list) {
         if (entry) {
-            *list = g_new0(GenericList, 1);
+            *list = g_malloc0(size);
             return true;
         } else {
             *list = NULL;
@@ -194,7 +194,7 @@ static bool qmp_input_start_list(Visitor *v, const char *name,
 }

 static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
-                                        Error **errp)
+                                        size_t size, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     StackObject *so = &qiv->stack[qiv->nb_stack - 1];
@@ -202,7 +202,7 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
     if (!so->entry) {
         return NULL;
     }
-    list->next = g_new0(GenericList, 1);
+    list->next = g_malloc0(size);
     return list->next;
 }

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index ce592d2..78567d0 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -120,7 +120,8 @@ static void qmp_output_end_struct(Visitor *v)
 }

 static bool qmp_output_start_list(Visitor *v, const char *name,
-                                  GenericList **listp, Error **errp)
+                                  GenericList **listp, size_t size,
+                                  Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QList *list = qlist_new();
@@ -131,7 +132,7 @@ static bool qmp_output_start_list(Visitor *v, const char *name,
 }

 static GenericList *qmp_output_next_list(Visitor *v, GenericList *list,
-                                         Error **errp)
+                                         size_t size, Error **errp)
 {
     return list->next;
 }
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 0e4a07c..86bddd1 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -121,7 +121,8 @@ error:
 }

 static bool
-start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
+start_list(Visitor *v, const char *name, GenericList **list, size_t size,
+           Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
     Error *err = NULL;
@@ -141,7 +142,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
         if (r) {
             siv->cur = r->begin;
         }
-        *list = g_new0(GenericList, 1);
+        *list = g_malloc0(size);
         return true;
     } else {
         *list = NULL;
@@ -150,7 +151,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 }

 static GenericList *
-next_list(Visitor *v, GenericList *list, Error **errp)
+next_list(Visitor *v, GenericList *list, size_t size, Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);
     Range *r;
@@ -176,7 +177,7 @@ next_list(Visitor *v, GenericList *list, Error **errp)
         siv->cur = r->begin;
     }

-    list->next = g_new0(GenericList, 1);
+    list->next = g_malloc0(size);
     return list->next;
 }

diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 2666802..104f8a4 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -264,7 +264,8 @@ static void print_type_number(Visitor *v, const char *name, double *obj,
 }

 static bool
-start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
+start_list(Visitor *v, const char *name, GenericList **list, size_t size,
+           Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);

@@ -280,7 +281,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
 }

 static GenericList *
-next_list(Visitor *v, GenericList *list, Error **errp)
+next_list(Visitor *v, GenericList *list, size_t size, Error **errp)
 {
     StringOutputVisitor *sov = to_sov(v);
     GenericList *ret = list->next;
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 5cf20c2..47a6523 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -26,11 +26,8 @@ def gen_array(name, element_type):
     return mcgen('''

 struct %(c_name)s {
-    union {
-        %(c_type)s value;
-        uint64_t padding;
-    };
     %(c_name)s *next;
+    %(c_type)s value;
 };
 ''',
                  c_name=c_name(name), c_type=element_type.c_type())
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index eebb5f7..193a1b6 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -130,7 +130,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     %(c_name)s *elt;
     bool allocated;

-    allocated = visit_start_list(v, name, (GenericList **)obj, &err);
+    allocated = visit_start_list(v, name, (GenericList **)obj, sizeof(%(c_name)s), &err);
     if (err) {
         goto out;
     }
@@ -140,7 +140,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         if (err) {
             break;
         }
-        elt = (%(c_name)s *)visit_next_list(v, (GenericList *)elt, &err);
+        elt = (%(c_name)s *)visit_next_list(v, (GenericList *)elt, sizeof(%(c_name)s), &err);
         if (err) {
             break;
         }
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (35 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types Eric Blake
@ 2016-01-19 16:10 ` Eric Blake
  2016-01-28 15:37   ` Markus Armbruster
  2016-01-28 17:56 ` [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster
  37 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-19 16:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, armbru, Michael Roth

Several commits have been changing the generator, but not updating
the docs to match:
- The implicit tag member is named "type", not "kind".  Screwed up in
commit 39a1815.
- Commit 9f08c8ec made list types lazy, and thereby dropped
UserDefOneList if nothing explicitly uses the list type.
- The parameter order has switched 'name' to occur earlier.
- The generated code now checks for partial allocations.
- etc.

Rework the examples to show slightly more output (we don't want to
show too much; that's what the testsuite is for), and regenerate the
output to match all recent changes.

Reported-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>

---
v9: new patch
---
 docs/qapi-code-gen.txt | 130 +++++++++++++++++++++++++++++--------------------
 1 file changed, 78 insertions(+), 52 deletions(-)

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 128f074..ae7b1a5 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1,7 +1,7 @@
 = How to use the QAPI code generator =

 Copyright IBM Corp. 2011
-Copyright (C) 2012-2015 Red Hat, Inc.
+Copyright (C) 2012-2016 Red Hat, Inc.

 This work is licensed under the terms of the GNU GPL, version 2 or
 later. See the COPYING file in the top-level directory.
@@ -655,7 +655,7 @@ Union types

     { "name": "BlockdevOptions", "meta-type": "object",
       "members": [
-          { "name": "kind", "type": "BlockdevOptionsKind" } ],
+          { "name": "type", "type": "BlockdevOptionsKind" } ],
       "tag": "type",
       "variants": [
           { "case": "file", "type": ":obj-FileOptions-wrapper" },
@@ -721,29 +721,34 @@ the names of built-in types.  Clients should examine member

 == Code generation ==

-Schemas are fed into four scripts to generate all the code/files that,
+Schemas are fed into five scripts to generate all the code/files that,
 paired with the core QAPI libraries, comprise everything required to
 take JSON commands read in by a Client JSON Protocol server, unmarshal
 the arguments into the underlying C types, call into the corresponding
-C function, and map the response back to a Client JSON Protocol
-response to be returned to the user.
+C function, map the response back to a Client JSON Protocol response
+to be returned to the user, and introspect the commands.

-As an example, we'll use the following schema, which describes a single
-complex user-defined type (which will produce a C struct, along with a list
-node structure that can be used to chain together a list of such types in
-case we want to accept/return a list of this type with a command), and a
-command which takes that type as a parameter and returns the same type:
+As an example, we'll use the following schema, which describes a
+single complex user-defined type, along with command which takes a
+list of that type as a parameter.  The user is responsible for writing
+the implementation of qmp_my_command(); everything else is produced by
+the generator.

     $ cat example-schema.json
     { 'struct': 'UserDefOne',
-      'data': { 'integer': 'int', 'string': 'str' } }
+      'data': { 'integer': 'int', '*string': 'str' } }

     { 'command': 'my-command',
-      'data':    {'arg1': 'UserDefOne'},
+      'data':    { 'arg1': ['UserDefOne'] },
       'returns': 'UserDefOne' }

     { 'event': 'MY_EVENT' }

+For a more thorough look at generated code, the testsuite includes
+tests/qapi-schema/qapi-schema-tests.json that covers more examples of
+what the generator will accept, and compiles the resulting C code as
+part of 'make check-unit'.
+
 === scripts/qapi-types.py ===

 Used to generate the C types defined by a schema. The following files are
@@ -776,7 +781,7 @@ Example:

         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOne(v, &obj, NULL, NULL);
+        visit_type_UserDefOne(v, NULL, &obj, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

@@ -791,7 +796,7 @@ Example:

         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOneList(v, &obj, NULL, NULL);
+        visit_type_UserDefOneList(v, NULL, &obj, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }
     $ cat qapi-generated/example-qapi-types.h
@@ -808,17 +813,15 @@ Example:

     struct UserDefOne {
         int64_t integer;
+        bool has_string;
         char *string;
     };

     void qapi_free_UserDefOne(UserDefOne *obj);

     struct UserDefOneList {
-        union {
-            UserDefOne *value;
-            uint64_t padding;
-        };
         UserDefOneList *next;
+        UserDefOne *value;
     };

     void qapi_free_UserDefOneList(UserDefOneList *obj);
@@ -854,54 +857,76 @@ Example:
     {
         Error *err = NULL;

-        visit_type_int(v, &(*obj)->integer, "integer", &err);
+        visit_type_int(v, "integer", &(*obj)->integer, &err);
         if (err) {
             goto out;
         }
-        visit_type_str(v, &(*obj)->string, "string", &err);
-        if (err) {
-            goto out;
+        if (visit_optional(v, "string", &(*obj)->has_string)) {
+            visit_type_str(v, "string", &(*obj)->string, &err);
+            if (err) {
+                goto out;
+            }
         }

     out:
         error_propagate(errp, err);
     }

-    void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp)
+    void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp)
     {
         Error *err = NULL;
+        bool allocated;

-        visit_start_struct(v, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err);
-        if (!err) {
-            if (*obj) {
-                visit_type_UserDefOne_fields(v, obj, errp);
-            }
-            visit_end_struct(v, &err);
+        allocated = visit_start_struct(v, name, (void **)obj, sizeof(UserDefOne),
+                                       &err);
+        if (err) {
+            goto out;
+        }
+        if (!*obj) {
+            goto out_obj;
+        }
+        visit_type_UserDefOne_fields(v, obj, &err);
+        if (err) {
+            goto out_obj;
         }
+        visit_check_struct(v, &err);
+    out_obj:
+        visit_end_struct(v);
+        if (allocated && err) {
+            qapi_free_UserDefOne(*obj);
+            *obj = NULL;
+        }
+    out:
         error_propagate(errp, err);
     }

-    void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp)
+    void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp)
     {
         Error *err = NULL;
-        GenericList *i, **prev;
+        UserDefOneList *eld;
+        bool allocated;

-        visit_start_list(v, name, &err);
+        allocated = visit_start_list(v, name, (GenericList **)obj, sizeof(UserDefOneList), &err);
         if (err) {
             goto out;
         }
-
-        for (prev = (GenericList **)obj;
-             !err && (i = visit_next_list(v, prev, &err)) != NULL;
-             prev = &i) {
-            UserDefOneList *native_i = (UserDefOneList *)i;
-            visit_type_UserDefOne(v, &native_i->value, NULL, &err);
+        elt = *obj;
+        while (elt) {
+            visit_type_UserDefOne(v, NULL, &elt->value, &err);
+            if (err) {
+                break;
+            }
+            elt = (UserDefOneList *)visit_next_list(v, (GenericList *)elt, sizeof(UserDefOneList), &err);
+            if (err) {
+                break;
+            }
         }
-
-        error_propagate(errp, err);
-        err = NULL;
-        visit_end_list(v, &err);
+        visit_end_list(v);
     out:
+        if (allocated && err) {
+            qapi_free_UserDefOneList(*obj);
+            *obj = NULL;
+        }
         error_propagate(errp, err);
     }
     $ cat qapi-generated/example-qapi-visit.h
@@ -912,8 +937,8 @@ Example:

 [Visitors for built-in types omitted...]

-    void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp);
-    void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp);
+    void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp);
+    void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp);

     #endif

@@ -949,7 +974,7 @@ Example:
         Visitor *v;

         v = qmp_output_get_visitor(qov);
-        visit_type_UserDefOne(v, &ret_in, "unused", &err);
+        visit_type_UserDefOne(v, "unused", &ret_in, &err);
         if (err) {
             goto out;
         }
@@ -960,7 +985,7 @@ Example:
         qmp_output_visitor_cleanup(qov);
         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOne(v, &ret_in, "unused", NULL);
+        visit_type_UserDefOne(v, "unused", &ret_in, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

@@ -971,10 +996,10 @@ Example:
         QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
         QapiDeallocVisitor *qdv;
         Visitor *v;
-        UserDefOne *arg1 = NULL;
+        UserDefOneList *arg1 = NULL;

         v = qmp_input_get_visitor(qiv);
-        visit_type_UserDefOne(v, &arg1, "arg1", &err);
+        visit_type_UserDefOne(v, "arg1", &arg1, &err);
         if (err) {
             goto out;
         }
@@ -991,7 +1016,7 @@ Example:
         qmp_input_visitor_cleanup(qiv);
         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
-        visit_type_UserDefOne(v, &arg1, "arg1", NULL);
+        visit_type_UserDefOne(v, "arg1", &arg1, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

@@ -1011,7 +1036,7 @@ Example:
     #include "qapi/qmp/qdict.h"
     #include "qapi/error.h"

-    UserDefOne *qmp_my_command(UserDefOne *arg1, Error **errp);
+    UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp);

     #endif

@@ -1095,8 +1120,9 @@ Example:
         "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
         "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
         "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
-        "{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
-        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
+        "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
+        "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
+        "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
         "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
         "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
     $ cat qapi-generated/example-qmp-introspect.h
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
@ 2016-01-20  9:02   ` Markus Armbruster
  2016-01-20 15:55     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20  9:02 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> We've already documented that our JSON parsing is locale dependent;
> but we should also document that our JSON output has the same
> problem.  Additionally, JSON requires finite values (you have to
> upgrade to JSON5 to get support for Inf or NaN), and our output
> risks truncating floating point numbers to the point of losing
> significant precision.
>
> Sadly, this series is not going to be the one that addresses these
> problems.
>
> Fix some trailing whitespace I noticed in the vicinity.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: no change
> v7: new patch
> ---
>  qobject/json-parser.c | 4 +++-
>  qobject/qjson.c       | 8 +++++++-
>  2 files changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/qobject/json-parser.c b/qobject/json-parser.c
> index 3c5d35d..6ab98a7 100644
> --- a/qobject/json-parser.c
> +++ b/qobject/json-parser.c
> @@ -1,5 +1,5 @@
>  /*
> - * JSON Parser 
> + * JSON Parser
>   *
>   * Copyright IBM, Corp. 2009
>   *
> @@ -519,6 +519,8 @@ static QObject *parse_literal(JSONParserContext *ctxt)
>      }
>      case JSON_FLOAT:
>          /* FIXME dependent on locale */
> +        /* FIXME our lexer matches RFC7159 in forbidding Inf or NaN,

For what it's worth, the RFC spells this "RFC 7159".

> +         * but those might be useful extensions beyond JSON */
>          return QOBJECT(qfloat_from_double(strtod(token->str, NULL)));
>      default:
>          abort();
> diff --git a/qobject/qjson.c b/qobject/qjson.c
> index a3e6a7c..41d9d65 100644
> --- a/qobject/qjson.c
> +++ b/qobject/qjson.c
> @@ -237,6 +237,12 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
>          char buffer[1024];
>          int len;
>
> +        /* FIXME: snprintf() is locale dependent; but JSON requires
> +         * numbers to be formatted as if in the C locale. */

The new FIXME matches the existing one in parse_literal(), visible
above.

However, dependence on C locale is a pervasive issue in QEMU.  These two
comments could give readers the idea that it's an issue just here.
Suggest to add something like "Dependence on C locale is a pervasive
issue in QEMU."

> +        /* FIXME: This risks printing Inf or NaN, which are not valid
> +         * JSON values. */
> +        /* FIXME: the default precision of %f may be insufficient to
> +         * tell this number apart from others. */

Yup.

The easy way to avoid loss of precision is %a, but of course that's way
too much sophistication for JSON.

Avoiding loss of precision with a decimal format is non-trivial; see
Steele, Jr., Guy L., and White, Jon L. How to print floating-point
numbers accurately, SIGPLAN ’90, and later improvements collected here:
http://stackoverflow.com/questions/7153979/algorithm-to-convert-an-ieee-754-double-to-a-string

>          len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val));
>          while (len > 0 && buffer[len - 1] == '0') {
>              len--;
> @@ -247,7 +253,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
>          } else {
>              buffer[len] = 0;
>          }
> -        
> +
>          qstring_append(str, buffer);
>          break;
>      }

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST()
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
@ 2016-01-20 10:04   ` Markus Armbruster
  2016-01-20 15:59     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 10:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The macro DO_UPCAST() is incorrectly named: it converts from a
> parent class to a derived class (which is a downcast).  Better,
> and more consistent with some of the other qapi visitors, is
> to use the container_of() macro through a to_FOO() helper.
>
> Our current definition of container_of() is weaker than
> DO_UPCAST(), in that it does not require the derived class to
> have Visitor as its first member, but this does not hurt our
> usage patterns in qapi visitors.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: no change
> v7: new patch
> ---
>  qapi/opts-visitor.c          | 28 +++++++++++++++++-----------
>  qapi/string-input-visitor.c  | 23 ++++++++++++++---------
>  qapi/string-output-visitor.c | 21 +++++++++++++--------
>  3 files changed, 44 insertions(+), 28 deletions(-)
>
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index ef5fb8b..dd4094c 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -89,6 +89,12 @@ struct OptsVisitor
>  };
>
>
> +static OptsVisitor *to_ov(Visitor *v)
> +{
> +    return container_of(v, OptsVisitor, visitor);
> +}
> +
> +

The name to_ov() is rather laconic even for my taste.  Tolerable, since
it's static.

>  static void
>  destroy_list(gpointer list)
>  {
> @@ -121,7 +127,7 @@ static void
>  opts_start_struct(Visitor *v, void **obj, const char *kind,
>                    const char *name, size_t size, Error **errp)
>  {
> -    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
> +    OptsVisitor *ov = to_ov(v);
>      const QemuOpt *opt;
>
>      if (obj) {

Much clearer!

[...]

Patch gets rid of all DO_UPCAST() in qapi/.  PATCH 33 brings one back;
it should use to_ov() instead.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor Eric Blake
@ 2016-01-20 13:05   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 13:05 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> Cache the visitor in a local variable instead of repeatedly
> calling the accessor.  Pass NULL for the visit_start_struct()
> object (which matches the fact that we were already passing 0
> for the size argument, because we aren't using the visit to
> allocate a qapi struct).

Two separate things.  Doing them both in a single patch is okay because
the resulting patch is short enough to be easily reviewable anyway.

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: no change
> v7: place earlier in series, drop attempts to provide a 'kind' string,
> drop bogus avoidance of qmp_object_del() on error
> v6: new patch, split from RFC on v5 7/46
> ---
>  hmp.c | 14 +++++++-------
>  1 file changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index 54f2620..6d67f9b 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1656,9 +1656,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
>      QemuOpts *opts;
>      char *type = NULL;
>      char *id = NULL;
> -    void *dummy = NULL;
>      OptsVisitor *ov;
>      QDict *pdict;
> +    Visitor *v;
>
>      opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
>      if (err) {
> @@ -1667,28 +1667,29 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
>
>      ov = opts_visitor_new(opts);
>      pdict = qdict_clone_shallow(qdict);
> +    v = opts_get_visitor(ov);
>
> -    visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
> +    visit_start_struct(v, NULL, NULL, NULL, 0, &err);


The only callers that can allocate are the generated visit_type_FOO()
for struct or union FOO.

The non-allocating callers mostly look like

    visit_start_struct(v, NULL, NULL, NULL, 0, &err);

Some pass a non-null @kind (third argument), but that's rubbish anyway
(see PATCH 17).

Some pass a non-null @name (fourth argument).  QMP visitors need that,
others don't care.

This caller is the only one that passes a non-null @obj (second
argument).  Makes visitors capable of allocating allocate @size bytes
(fifth argument).  Except opts-visitor allocates one byte instead of
zero to avoid a null object, for unclear reasons.  Whatever gets
allocated we free (see last hunk) without using.  The patch avoids this
pointless allocation.  Good.  Suggest to explain this more clearly in
the commit message:

    Pass NULL for the visit_start_struct() parameter @obj to suppress
    unwanted allocation, like we do elsewhere.

>      if (err) {
>          goto out_clean;
>      }
>
>      qdict_del(pdict, "qom-type");
> -    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
> +    visit_type_str(v, &type, "qom-type", &err);
>      if (err) {
>          goto out_end;
>      }
>
>      qdict_del(pdict, "id");
> -    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
> +    visit_type_str(v, &id, "id", &err);
>      if (err) {
>          goto out_end;
>      }
>
> -    object_add(type, id, pdict, opts_get_visitor(ov), &err);
> +    object_add(type, id, pdict, v, &err);
>
>  out_end:
> -    visit_end_struct(opts_get_visitor(ov), &err_end);
> +    visit_end_struct(v, &err_end);
>      if (!err && err_end) {
>          qmp_object_del(id, NULL);
>      }
> @@ -1700,7 +1701,6 @@ out_clean:
>      qemu_opts_del(opts);
>      g_free(id);
>      g_free(type);
> -    g_free(dummy);
>
>  out:
>      hmp_handle_error(mon, &err);

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/37] vl: Improve use of qapi visitor
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 05/37] vl: " Eric Blake
@ 2016-01-20 13:43   ` Markus Armbruster
  2016-01-20 16:18     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 13:43 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Paolo Bonzini

Eric Blake <eblake@redhat.com> writes:

> Cache the visitor in a local variable instead of repeatedly
> calling the accessor.  Pass NULL for the visit_start_struct()
> object (which matches the fact that we were already passing 0
> for the size argument, because we aren't using the visit to
> allocate a qapi struct).  Guarantee that visit_end_struct()
> is called if visit_start_struct() succeeded.

Three separate things --- you're pushing it now :)

Impact of not calling visit_end_struct()?

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: no change
> v7: place earlier in series, drop attempts to provide a 'kind' string,
> drop bogus avoidance of qmp_object_del() on error
> v6: new patch, split from RFC on v5 7/46
> ---
>  vl.c | 26 ++++++++++++++------------
>  1 file changed, 14 insertions(+), 12 deletions(-)
>
> diff --git a/vl.c b/vl.c
> index f043009..aaa5403 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2822,44 +2822,47 @@ static bool object_create_delayed(const char *type)
>  static int object_create(void *opaque, QemuOpts *opts, Error **errp)
>  {
>      Error *err = NULL;
> +    Error *err_end = NULL;
>      char *type = NULL;
>      char *id = NULL;
> -    void *dummy = NULL;
>      OptsVisitor *ov;
>      QDict *pdict;
>      bool (*type_predicate)(const char *) = opaque;
> +    Visitor *v;
>
>      ov = opts_visitor_new(opts);
>      pdict = qemu_opts_to_qdict(opts, NULL);
> +    v = opts_get_visitor(ov);
>
> -    visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
> +    visit_start_struct(v, NULL, NULL, NULL, 0, &err);

Same argument as for the previous patch.

>      if (err) {
>          goto out;
>      }
>
>      qdict_del(pdict, "qom-type");
> -    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
> +    visit_type_str(v, &type, "qom-type", &err);
>      if (err) {
>          goto out;
>      }

If we get here, we must call visit_end_struct().

>      if (!type_predicate(type)) {
> +        visit_end_struct(v, NULL);

Here, you add the previously missing visit_end_struct() to the error
path.

>          goto out;
>      }
>
>      qdict_del(pdict, "id");
> -    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
> +    visit_type_str(v, &id, "id", &err);
>      if (err) {
> -        goto out;
> +        goto out_end;

Here, you skip to the function's cleanup, which now includes
visit_end_struct().

Such a mix is can be a sign the cleanup isn't quite in the right order.

When we take this error path to out_end, then:

   out_end:
       visit_end_struct(v, &err_end);   // called, as we must
       if (!err && err_end) {           // !err is false
           qmp_object_del(id, NULL);    // not called
       }
       error_propagate(&err, err_end);  // since err, this is
                                        // error_free(err_end)

>      }
>
> -    object_add(type, id, pdict, opts_get_visitor(ov), &err);
> -    if (err) {
> -        goto out;
> -    }
> -    visit_end_struct(opts_get_visitor(ov), &err);
> -    if (err) {
> +    object_add(type, id, pdict, v, &err);
> +
> +out_end:
> +    visit_end_struct(v, &err_end);

visit_end_struct() must be called when visit_start_struct() succeeded.
All error paths after that success pass through here, except for one,
and that one calls visit_end_struct().  Okay.

> +    if (!err && err_end) {
>          qmp_object_del(id, NULL);
>      }

qmp_object_del() must be called when we fail after object_add()
succeeded.  That's what the condition does.

> +    error_propagate(&err, err_end);
>
>  out:

Cleanup below here must be done always.

>      opts_visitor_cleanup(ov);

The only reason I'm not asking you to rewrite this mess is that you're
already rewriting it later in this series.

> @@ -2867,7 +2870,6 @@ out:
>      QDECREF(pdict);
>      g_free(id);
>      g_free(type);
> -    g_free(dummy);
>      if (err) {
>          error_report_err(err);
>          return -1;

I wonder whether splitting this and the previous patch along the change
rather than the file would come out nicer:

1. Cache the visitor

2. Suppress the pointless allocation

3. Fix to call visit_end_struct()

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 06/37] balloon: Improve use of qapi visitor
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 06/37] balloon: " Eric Blake
@ 2016-01-20 14:09   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 14:09 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael S. Tsirkin

Eric Blake <eblake@redhat.com> writes:

> Rework the control flow of balloon_stats_get_all() to make it
> easier for a later patch to split visit_end_struct().  Also
> switch to the uint64 visitor to match the data type.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: no change
> v7: place earlier in series
> v6: new patch, split from RFC on v5 7/46
> ---
>  hw/virtio/virtio-balloon.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
> index 9671635..1ce987a 100644
> --- a/hw/virtio/virtio-balloon.c
> +++ b/hw/virtio/virtio-balloon.c
> @@ -130,10 +130,13 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v,
>      if (err) {
>          goto out_end;
>      }
> -    for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
> -        visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
> -                         &err);
> +    for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
> +        visit_type_uint64(v, &s->stats[i], balloon_stat_names[i], &err);
> +        if (err) {
> +            goto out_nested;
> +        }
>      }
> +out_nested:
>      error_propagate(errp, err);
>      err = NULL;
>      visit_end_struct(v, &err);

What's wrong with plain break?

Hmm, PATCH 33 inserts something between the loop and the label.  I
would've used break here regardless of the extra churn, just to avoid
review hiccups like this one.  Too late now :)

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event use of qapi visitor
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event " Eric Blake
@ 2016-01-20 15:19   ` Markus Armbruster
  2016-01-20 17:10     ` Eric Blake
  2016-01-28 22:51     ` Eric Blake
  0 siblings, 2 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 15:19 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

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.

Disgusting, isn't it?  :)

>                               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).
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> 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 | 12 +++++-------
>  scripts/qapi.py       |  5 +++--
>  2 files changed, 8 insertions(+), 9 deletions(-)
>
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index 720486f..761cda9 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -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);
> -

Good riddance: qmp_output_visitor_new() can't fail.  If that ever
changes, qmp_output_get_visitor() will crash just fine.

>      v = qmp_output_get_visitor(qov);
> -    g_assert(v);

Good riddance^2: qmp_output_get_visitor() can't fail, either.

>
> -    /* 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')

On error, we now go to out_obj rather than out.  Some fields will be
unvisited then (possibly all), and err will be set.

Now I get to figure out what this change changes.

>          ret += mcgen('''
> +out_obj:
>      visit_end_struct(v, &err);
>      if (err) {
>          goto out;
>      }

Good: we actually call visit_end_struct() as we should.

Not so good: if we get here via the error exit of gen_visit_fields(),
err is set.  If visit_end_struct() tries to set another error...

I guess the idea is to go from gen_visit_fields() failure through
visit_end_struct() here to out.  Correct?

>
>      obj = qmp_output_get_qobject(qov);
> -    g_assert(obj != NULL);
> +    g_assert(obj);
>
>      qdict_put_obj(qmp, "data", obj);
>  ''')

       ret += mcgen('''
       emit(%(c_enum)s, qmp, &err);

   ''',
                    c_enum=c_enum_const(event_enum_name, name))

       if arg_type and arg_type.members:
           ret += mcgen('''
   out:
       qmp_output_visitor_cleanup(qov);
   ''')
       ret += mcgen('''
       error_propagate(errp, err);
       QDECREF(qmp);
   }
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 7dec611..497eaba 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'):

Probably clearer than label=None, but duplicates gen_err_check()'s
default.  Fine with me.

>      ret = ''
>      if skiperr:
>          errparg = 'NULL'
       else:
           errparg = '&err'

       for memb in members:
           if memb.optional:
               ret += mcgen('''
       if (visit_optional(v, "%(name)s", &%(prefix)shas_%(c_name)s)) {
   ''',
                            prefix=prefix, c_name=c_name(memb.name),
                            name=memb.name, errp=errparg)
               push_indent()

errp=errparg unused here.  Not this patch's job to clean up.

> @@ -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()

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling
  2016-01-20  9:02   ` Markus Armbruster
@ 2016-01-20 15:55     ` Eric Blake
  2016-01-21  6:21       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 15:55 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Luiz Capitulino

[-- Attachment #1: Type: text/plain, Size: 3852 bytes --]

On 01/20/2016 02:02 AM, Markus Armbruster wrote:

>> @@ -519,6 +519,8 @@ static QObject *parse_literal(JSONParserContext *ctxt)
>>      }
>>      case JSON_FLOAT:
>>          /* FIXME dependent on locale */
>> +        /* FIXME our lexer matches RFC7159 in forbidding Inf or NaN,
> 
> For what it's worth, the RFC spells this "RFC 7159".

Looks like we use space more often than not, but that we're
inconsistent.  For example:

slirp/tcp.h: * Per RFC 793, September, 1981.
slirp/tcp.h: * Per RFC793, September, 1981.

Will fix if I need to respin, otherwise I assume you can do it.


>> +        /* FIXME: snprintf() is locale dependent; but JSON requires
>> +         * numbers to be formatted as if in the C locale. */
> 
> The new FIXME matches the existing one in parse_literal(), visible
> above.
> 
> However, dependence on C locale is a pervasive issue in QEMU.  These two
> comments could give readers the idea that it's an issue just here.
> Suggest to add something like "Dependence on C locale is a pervasive
> issue in QEMU."

Good idea.

> 
>> +        /* FIXME: This risks printing Inf or NaN, which are not valid
>> +         * JSON values. */
>> +        /* FIXME: the default precision of %f may be insufficient to
>> +         * tell this number apart from others. */
> 
> Yup.
> 
> The easy way to avoid loss of precision is %a, but of course that's way
> too much sophistication for JSON.
> 
> Avoiding loss of precision with a decimal format is non-trivial; see
> Steele, Jr., Guy L., and White, Jon L. How to print floating-point
> numbers accurately, SIGPLAN ’90, and later improvements collected here:
> http://stackoverflow.com/questions/7153979/algorithm-to-convert-an-ieee-754-double-to-a-string

Ah, memories.  I read and implemented that paper when working on the
jikes compiler for the Java language back in the late nineties, as it is
the Java language specification which had the very neat property of
requiring the shortest decimal string that will unambiguously round back
to the same floating point pattern.

One alternative is to always output a guaranteed unambiguous decimal
string (although not necessarily the shortest), by using %.17f, using
<float.h> DBL_DECIMAL_DIG.  (Note that DBL_DIG of 15 is NOT sufficient -
it is the lower limit that says that a decimal->float->decimal will not
change the decimal; but we want the converse where a
float->decimal->float will not change the float.  There are stretches of
numbers where the pigeonhole principle applies; you can think of it this
way: there is no way to map all possible 2^10 (1024) binary values
inside 2^3 (1000) decimal digits without at least 24 of them needing one
more decimal digit.  But by the same arguments, DBL_DECIMAL_DIG is an
upper limit and usually more than you need.)

So, the question is whether we want to always output 17 digits, or
whether we want to do the poor-man's truncation scheme (easy to
understand, but not optimal use of the processor), or go all the way to
the algorithm of that paper (faster but lots harder to understand).  For
reference, here's the poor-man's algorithm in pseudocode:

if 0, inf, nan:
    special case
else:
    Obtain the DBL_DECIMAL_DIG string via sprintf %.17f
    i = 17;
    do {
        truncate the original string to i-1 decimal characters
        parse that with strtod()
        if the bit pattern differs:
            break;
    } while (--i);
    assert(i)
    use i digits of the string

As a separate patch, of course, but I have a pending patch that provides
a single place where we could drop in such an improvement:
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg03932.html

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST()
  2016-01-20 10:04   ` Markus Armbruster
@ 2016-01-20 15:59     ` Eric Blake
  2016-01-21  6:22       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 15:59 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1563 bytes --]

On 01/20/2016 03:04 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> The macro DO_UPCAST() is incorrectly named: it converts from a
>> parent class to a derived class (which is a downcast).  Better,
>> and more consistent with some of the other qapi visitors, is
>> to use the container_of() macro through a to_FOO() helper.
>>
>> Our current definition of container_of() is weaker than
>> DO_UPCAST(), in that it does not require the derived class to
>> have Visitor as its first member, but this does not hurt our
>> usage patterns in qapi visitors.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>

>>
>> +static OptsVisitor *to_ov(Visitor *v)
>> +{
>> +    return container_of(v, OptsVisitor, visitor);
>> +}
>> +
>> +
> 
> The name to_ov() is rather laconic even for my taste.  Tolerable, since
> it's static.

And matches existing practice pre-patch:

qapi/qapi-dealloc-visitor.c:static QapiDeallocVisitor *to_qov(Visitor *v)
qapi/qmp-input-visitor.c:static QmpInputVisitor *to_qiv(Visitor *v)
qapi/qmp-output-visitor.c:static QmpOutputVisitor *to_qov(Visitor *v)


> 
> Patch gets rid of all DO_UPCAST() in qapi/.  PATCH 33 brings one back;
> it should use to_ov() instead.

D'oh. You can tell what order I wrote the patches in :)

If I need to respin, I'll fix it; otherwise, I assume you can handle it.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop Eric Blake
@ 2016-01-20 16:03   ` Markus Armbruster
  2016-01-20 17:15     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 16:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Inside the generated code between visit_start_struct() and
> visit_end_struct(), we were blindly setting the error into
> the caller's errp parameter.  But a future patch to split
> visit_end_struct() will require that we take action based
> on whether an error has occurred, which requires us to track
> all actions through a local err.  Rewrite the visits to be
> more in line with the other generated calls.
>
> Generated code changes look like:
>
> |@@ -42,12 +42,18 @@ void visit_type_GuestAgentCommandInfo(Vi

I'd drop this line.

> |     Error *err = NULL;
> |
> |     visit_start_struct(v, (void **)obj, "GuestAgentCommandInfo", name, sizeof(GuestAgentCommandInfo), &err);
> |-    if (!err) {
> |-        if (*obj) {
> |-            visit_type_GuestAgentCommandInfo_fields(v, obj, errp);
> |-        }
> |-        visit_end_struct(v, &err);
> |+    if (err) {
> |+        goto out;
> |     }
> |+    if (!*obj) {

err is clear here.

> |+        goto out_obj;
> |+    }
> |+    visit_type_GuestAgentCommandInfo_fields(v, obj, &err);
> |+out_obj:
> |+    error_propagate(errp, err);
> |+    err = NULL;

If we come from goto out_obj, these two lines are no-ops.

> |+    visit_end_struct(v, &err);
> |+out:
> |     error_propagate(errp, err);
> | }
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: enhance commit message
> v8: no change
> v7: place earlier in series
> v6: based loosely on v5 7/46, but mostly a rewrite to get the last
> generated code in the same form as all the others, so that the
> later conversion to split visit_check_struct() will be easier
> ---
>  scripts/qapi-visit.py | 16 +++++++++++-----
>  1 file changed, 11 insertions(+), 5 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index b93690b..4a4f67d 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -123,12 +123,18 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
>      Error *err = NULL;
>
>      visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
> -    if (!err) {
> -        if (*obj) {
> -            visit_type_%(c_name)s_fields(v, obj, errp);
> -        }
> -        visit_end_struct(v, &err);
> +    if (err) {
> +        goto out;
>      }
> +    if (!*obj) {
> +        goto out_obj;
> +    }
> +    visit_type_%(c_name)s_fields(v, obj, &err);
> +out_obj:
> +    error_propagate(errp, err);
> +    err = NULL;
> +    visit_end_struct(v, &err);
> +out:
>      error_propagate(errp, err);
>  }
>  ''',

gen_visit_union(), the other generator of visit_start_struct(), already
does it this way.  It generates additional goto out_obj, so the
placement of out_obj makes more sense there.  I guess we want to place
it in the same spot here to facilitate unifying the two.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/37] vl: Improve use of qapi visitor
  2016-01-20 13:43   ` Markus Armbruster
@ 2016-01-20 16:18     ` Eric Blake
  2016-01-21  6:45       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 16:18 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Paolo Bonzini

[-- Attachment #1: Type: text/plain, Size: 4248 bytes --]

On 01/20/2016 06:43 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Cache the visitor in a local variable instead of repeatedly
>> calling the accessor.  Pass NULL for the visit_start_struct()
>> object (which matches the fact that we were already passing 0
>> for the size argument, because we aren't using the visit to
>> allocate a qapi struct).  Guarantee that visit_end_struct()
>> is called if visit_start_struct() succeeded.
> 
> Three separate things --- you're pushing it now :)

Two of them the same as in 4/37.

So, among the five items, I did a split in two based on file.  I could
split in the other direction:

fix hmp and vl to cache their visitor
fix hmp and vl to pass NULL to avoid pointless allocation
fix vl to guarantee visit_end_struct

> 
> Impact of not calling visit_end_struct()?

Assertion failures after patch 24.  :)
Basically, I wanted to see whether the code base could get simpler if we
always enforced visit start/end grouping, and there were only a couple
of outliers that needed fixing (here, and in patch 7).


>>
>>      qdict_del(pdict, "qom-type");
>> -    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
>> +    visit_type_str(v, &type, "qom-type", &err);
>>      if (err) {
>>          goto out;
>>      }
> 
> If we get here, we must call visit_end_struct().
> 
>>      if (!type_predicate(type)) {
>> +        visit_end_struct(v, NULL);
> 
> Here, you add the previously missing visit_end_struct() to the error
> path.
> 
>>          goto out;
>>      }
>>
>>      qdict_del(pdict, "id");
>> -    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
>> +    visit_type_str(v, &id, "id", &err);
>>      if (err) {
>> -        goto out;
>> +        goto out_end;
> 
> Here, you skip to the function's cleanup, which now includes
> visit_end_struct().
> 
> Such a mix is can be a sign the cleanup isn't quite in the right order.
> 
> When we take this error path to out_end, then:
> 
>    out_end:
>        visit_end_struct(v, &err_end);   // called, as we must
>        if (!err && err_end) {           // !err is false
>            qmp_object_del(id, NULL);    // not called
>        }
>        error_propagate(&err, err_end);  // since err, this is
>                                         // error_free(err_end)

Correct. And it gets simpler later on, when visit_end_struct() loses the
errp argument (patch 33).

> 
>>      }
>>
>> -    object_add(type, id, pdict, opts_get_visitor(ov), &err);
>> -    if (err) {
>> -        goto out;
>> -    }
>> -    visit_end_struct(opts_get_visitor(ov), &err);
>> -    if (err) {
>> +    object_add(type, id, pdict, v, &err);
>> +
>> +out_end:
>> +    visit_end_struct(v, &err_end);
> 
> visit_end_struct() must be called when visit_start_struct() succeeded.
> All error paths after that success pass through here, except for one,
> and that one calls visit_end_struct().  Okay.
> 
>> +    if (!err && err_end) {
>>          qmp_object_del(id, NULL);
>>      }
> 
> qmp_object_del() must be called when we fail after object_add()
> succeeded.  That's what the condition does.
> 
>> +    error_propagate(&err, err_end);
>>
>>  out:
> 
> Cleanup below here must be done always.
> 
>>      opts_visitor_cleanup(ov);
> 
> The only reason I'm not asking you to rewrite this mess is that you're
> already rewriting it later in this series.

So I think you found patch 33.

> 
>> @@ -2867,7 +2870,6 @@ out:
>>      QDECREF(pdict);
>>      g_free(id);
>>      g_free(type);
>> -    g_free(dummy);
>>      if (err) {
>>          error_report_err(err);
>>          return -1;
> 
> I wonder whether splitting this and the previous patch along the change
> rather than the file would come out nicer:
> 
> 1. Cache the visitor
> 
> 2. Suppress the pointless allocation
> 
> 3. Fix to call visit_end_struct()
> 

What do you know - we're thinking on the same lines :)  [And now you
know I just replied to your email in the order that I read it, rather
than reading the whole thing first]

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors Eric Blake
@ 2016-01-20 17:07   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 17:07 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The qapi builtin type 'int' is basically shorthand for the type
> 'int64'.  In fact, since no visitor was providing the optional
> type_int64() callback, visit_type_int64() was just always falling
> back to type_int(), cementing the equivalence between the types.
>
> However, some visitors are providing a type_uint64() callback.
> For purposes of code consistency, it is nicer if all visitors
> use the paired type_int64/type_uint64 names rather than the
> mismatched type_int/type_uint64.  So this patch just renames
> the signed int callbacks in place, dropping the type_int()
> callback as redundant, and a later patch will focus on the
> unsigned int callbacks.
>
> Add some FIXMEs to questionable reuse of errp in code touched
> by the rename, while at it (the reuse works as long as the
> callbacks don't modify value when setting an error, but it's not
> a good example to set) - a later patch will then fix those.
>
> No change in functionality here, although further cleanups are
> in the pipeline.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 6d63e40..6295fa8 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -97,7 +97,7 @@ void visit_type_enum(Visitor *v, int *obj, const char * const strings[],
>
>  void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>  {
> -    v->type_int(v, obj, name, errp);
> +    v->type_int64(v, obj, name, errp);
>  }
>
>  void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
> @@ -108,8 +108,10 @@ void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
>          v->type_uint8(v, obj, name, errp);
>      } else {
>          value = *obj;
> -        v->type_int(v, &value, name, errp);
> +        v->type_int64(v, &value, name, errp);
>          if (value < 0 || value > UINT8_MAX) {
> +            /* FIXME questionable reuse of errp if type_int64() changes
> +               value on error */
>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
>                         name ? name : "null", "uint8_t");

Changing value on error would be in bad taste, and could be outlawed in
the contract.  But you demonstrate in PATCH 11 that there's no need to
depend on it not being changed.

>              return;
[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event use of qapi visitor
  2016-01-20 15:19   ` Markus Armbruster
@ 2016-01-20 17:10     ` Eric Blake
  2016-01-21  7:16       ` Markus Armbruster
  2016-01-26 23:40       ` Eric Blake
  2016-01-28 22:51     ` Eric Blake
  1 sibling, 2 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-20 17:10 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 3943 bytes --]

On 01/20/2016 08:19 AM, Markus Armbruster wrote:
> 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.
> 
> Disgusting, isn't it?  :)

This, along with the fix in 5/37, were the two places that had to be
fixed to avoid assertions in patch 24, when I turned on stricter
enforcing of cleanup only on an evenly matched 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).
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> ---
>> 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.

Dead sentence leftover from v8; as mentioned above, I DID sink the
declaration reordering to a later series for v9.  But it's after the
---, so it gets trimmed automatically by 'git am'.


>>          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')
> 
> On error, we now go to out_obj rather than out.  Some fields will be
> unvisited then (possibly all), and err will be set.
> 
> Now I get to figure out what this change changes.
> 
>>          ret += mcgen('''
>> +out_obj:
>>      visit_end_struct(v, &err);
>>      if (err) {
>>          goto out;
>>      }
> 
> Good: we actually call visit_end_struct() as we should.
> 
> Not so good: if we get here via the error exit of gen_visit_fields(),
> err is set.  If visit_end_struct() tries to set another error...

Oops. It all gets cleaned up in 33 when visit_end_struct() loses the
errp argument, but in the meantime, I think the most robust way to write
this would be:

out_obj:
    visit_end_struct(v, err ? NULL : &err);
    if (err) {
...

> 
> I guess the idea is to go from gen_visit_fields() failure through
> visit_end_struct() here to out.  Correct?

Yes.

>> +++ 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'):
> 
> Probably clearer than label=None, but duplicates gen_err_check()'s
> default.  Fine with me.
> 
>>      ret = ''
>>      if skiperr:
>>          errparg = 'NULL'
>        else:
>            errparg = '&err'
> 
>        for memb in members:
>            if memb.optional:
>                ret += mcgen('''
>        if (visit_optional(v, "%(name)s", &%(prefix)shas_%(c_name)s)) {
>    ''',
>                             prefix=prefix, c_name=c_name(memb.name),
>                             name=memb.name, errp=errparg)
>                push_indent()
> 
> errp=errparg unused here.  Not this patch's job to clean up.

Bah. Commit 5cdc8831 missed it, due to repeated refactoring. I'm a bit
surprised that pep8 didn't complain.  Okay, I'm adding it as a separate
cleanup.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop
  2016-01-20 16:03   ` Markus Armbruster
@ 2016-01-20 17:15     ` Eric Blake
  2016-01-21  7:19       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 17:15 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1500 bytes --]

On 01/20/2016 09:03 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Inside the generated code between visit_start_struct() and
>> visit_end_struct(), we were blindly setting the error into
>> the caller's errp parameter.  But a future patch to split
>> visit_end_struct() will require that we take action based
>> on whether an error has occurred, which requires us to track
>> all actions through a local err.  Rewrite the visits to be
>> more in line with the other generated calls.
>>

>> |+    if (!*obj) {
> 
> err is clear here.
> 
>> |+        goto out_obj;
>> |+    }
>> |+    visit_type_GuestAgentCommandInfo_fields(v, obj, &err);
>> |+out_obj:
>> |+    error_propagate(errp, err);
>> |+    err = NULL;
> 
> If we come from goto out_obj, these two lines are no-ops.

I could move the label, if desired...

> 
>> |+    visit_end_struct(v, &err);
>> |+out:
>> |     error_propagate(errp, err);
>> | }

> 
> gen_visit_union(), the other generator of visit_start_struct(), already
> does it this way.  It generates additional goto out_obj, so the
> placement of out_obj makes more sense there.  I guess we want to place
> it in the same spot here to facilitate unifying the two.

...but as you observed, the unification in 31 is a bit easier with it
placed identically.  Maybe a commit message comment is in order.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks Eric Blake
@ 2016-01-20 17:29   ` Markus Armbruster
  2016-01-20 18:10     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 17:29 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Our qapi visitor contract supports multiple integer visitors,
> but left the type_uint64 visitor as optional (falling back on
> type_int64); it also marks the type_size visitor as optional
> (falling back on type_uint64 or even type_int64).
>
> Note that the default of falling back on type_int for unsigned
> visitors can cause confusing results for values larger than
> INT64_MAX (such as having to pass in a negative 2s complement
> value on input, and getting a negative result on output).
>
> This patch does not fully address the disparity in handling
> large values as negatives, but does move towards a cleaner
> situation where EVERY visitor provides both type_int64 and
> type_uint64 variants as entry points; then each client can
> either implement sane differences between the two, or document
> in place with a FIXME that there is munging going on.

Before: nobody implements type_uint64(), and the core falls back to
type_int64(), casting negative values to large positive ones.  With an
implementation of type_int64() that parses large positive values as
negative, the two casts cancel out.

After: everybody implements type_uint64() incorrectly, by reusing
type_int64() code.  The problem moves from visitor core to visitor
implementations, where we can actually fix it if we care.

Correct?

> The dealloc visitor no longer needs a type_size callback,
> since that now safely falls back to the type_uint64 callback.

Did it need the callback before this patch?

> Then, in qapi-visit-core.c, we can now use the guaranteed
> type_uint64 callback as the fallback for all smaller unsigned
> int visits.

The type_int64() callback works just fine for smaller unsigned ints, but
I agree avoiding mixed signedness by using type_uint64() make sense once
type_uint64() is mandatory.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: hoist in part of 11/35, drop Marc-Andre's R-b
> v8: no change
> v7: split off int64 callbacks and retitle, add more FIXMEs in the
> code, hoist use of type_uint64 here from 3/23, improved commit
> message
> v6: new patch, but stems from v5 23/46
> ---
>  include/qapi/visitor-impl.h  |  9 ++++++---
>  qapi/qapi-dealloc-visitor.c  | 12 ++++++------
>  qapi/qapi-visit-core.c       | 42 ++++++++++++++----------------------------
>  qapi/qmp-input-visitor.c     | 17 +++++++++++++++++
>  qapi/qmp-output-visitor.c    |  9 +++++++++
>  qapi/string-input-visitor.c  | 15 +++++++++++++++
>  qapi/string-output-visitor.c |  9 +++++++++
>  7 files changed, 76 insertions(+), 37 deletions(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index c263a26..45c1d3e 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -40,6 +40,12 @@ struct Visitor
>      void (*type_int64)(Visitor *v, int64_t *obj, const char *name,
>                         Error **errp);
>      /* Must be set. */
> +    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name,
> +                        Error **errp);
> +    /* Optional; fallback is type_uint64().  */
> +    void (*type_size)(Visitor *v, uint64_t *obj, const char *name,
> +                      Error **errp);
> +    /* Must be set. */
>      void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
>      void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
>      void (*type_number)(Visitor *v, double *obj, const char *name,
> @@ -53,12 +59,9 @@ struct Visitor
>      void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
>      void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
>      void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
> -    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
>      void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
>      void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
>      void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
> -    /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */
> -    void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
>      bool (*start_union)(Visitor *v, bool data_present, Error **errp);
>      void (*end_union)(Visitor *v, bool data_present, Error **errp);
>  };
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index e9b9f3f..11eb828 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -140,6 +140,11 @@ static void qapi_dealloc_type_int64(Visitor *v, int64_t *obj, const char *name,
>  {
>  }
>
> +static void qapi_dealloc_type_uint64(Visitor *v, uint64_t *obj,
> +                                     const char *name, Error **errp)
> +{
> +}
> +
>  static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name,
>                                     Error **errp)
>  {
> @@ -158,11 +163,6 @@ static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
>      }
>  }
>
> -static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
> -                                   Error **errp)
> -{
> -}
> -
>  static void qapi_dealloc_type_enum(Visitor *v, int *obj,
>                                     const char * const strings[],
>                                     const char *kind, const char *name,
> @@ -220,11 +220,11 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
>      v->visitor.end_list = qapi_dealloc_end_list;
>      v->visitor.type_enum = qapi_dealloc_type_enum;
>      v->visitor.type_int64 = qapi_dealloc_type_int64;
> +    v->visitor.type_uint64 = qapi_dealloc_type_uint64;
>      v->visitor.type_bool = qapi_dealloc_type_bool;
>      v->visitor.type_str = qapi_dealloc_type_str;
>      v->visitor.type_number = qapi_dealloc_type_number;
>      v->visitor.type_any = qapi_dealloc_type_anything;
> -    v->visitor.type_size = qapi_dealloc_type_size;
>      v->visitor.start_union = qapi_dealloc_start_union;
>
>      QTAILQ_INIT(&v->stack);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 6295fa8..4a8ad43 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -102,15 +102,15 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>
>  void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
>  {
> -    int64_t value;
> +    uint64_t value;
>
>      if (v->type_uint8) {
>          v->type_uint8(v, obj, name, errp);
>      } else {
>          value = *obj;
> -        v->type_int64(v, &value, name, errp);
> -        if (value < 0 || value > UINT8_MAX) {
> -            /* FIXME questionable reuse of errp if type_int64() changes
> +        v->type_uint64(v, &value, name, errp);
> +        if (value > UINT8_MAX) {
> +            /* FIXME questionable reuse of errp if type_uint64() changes
>                 value on error */
>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
>                         name ? name : "null", "uint8_t");

You could delay adding these FIXMEs until this patch, and reduce churn.
Probably not worth the bother now.

[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks Eric Blake
@ 2016-01-20 17:34   ` Markus Armbruster
  2016-01-20 18:17     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 17:34 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Commit 4e27e819 introduced optional visitor callbacks for all
> sorts of int types, but no visitor has supplied any of the
> callbacks for sizes less than 64 bits.  In other words, the
> generic implementation based on using type_[u]int64() followed
> by bounds-checking works just fine. In the interest of
> simplicity, it's easier to make the visitor callback interface
> not have to worry about the other sizes.
>
> Adding some helper functions minimizes the boilerplate required
> to correct FIXMEs added earlier with regards to questionable
> reuse of errp, particularly now that we can guarantee from a
> single file audit that value is unchanged if an error is set.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: hoist some of visitor-impl.h changes into 9/35 and 10/35
> v8: no change
> v7: further factor out helper functions that eliminate the
> questionable errp reuse
> v6: split off from v5 23/46
> original version also appeared in v6-v9 of subset D
> ---
>  include/qapi/visitor-impl.h |   8 +--
>  qapi/qapi-visit-core.c      | 158 +++++++++++++++++---------------------------
>  2 files changed, 60 insertions(+), 106 deletions(-)

Nice diffstat.

>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 45c1d3e..e6399d1 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -1,7 +1,7 @@
>  /*
>   * Core Definitions for QAPI Visitor implementations
>   *
> - * Copyright (C) 2012 Red Hat, Inc.
> + * Copyright (C) 2012, 2015-2016 Red Hat, Inc.

git-log has authors @redhat.com in 2013 and 2014 as well.

>   *
>   * Author: Paolo Bonizni <pbonzini@redhat.com>
>   *
> @@ -56,12 +56,6 @@ struct Visitor
>      /* May be NULL; most useful for input visitors. */
>      void (*optional)(Visitor *v, bool *present, const char *name);
>
> -    void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
> -    void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
> -    void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
> -    void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
> -    void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
> -    void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
>      bool (*start_union)(Visitor *v, bool data_present, Error **errp);
>      void (*end_union)(Visitor *v, bool data_present, Error **errp);
>  };
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 4a8ad43..a48fd4e 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -100,129 +100,89 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      v->type_int64(v, obj, name, errp);
>  }
>
> +static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name,
> +                             uint64_t max, const char *type, Error **errp)
> +{
> +    Error *err = NULL;
> +    uint64_t value = *obj;
> +
> +    v->type_uint64(v, &value, name, &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +    } else if (value > max) {
> +        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
> +                   name ? name : "null", type);

We should clean up this name ? name : "null" nonsense some day.

> +    } else {
> +        *obj = value;
> +    }
> +}
[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int*
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int* Eric Blake
@ 2016-01-20 18:08   ` Markus Armbruster
  2016-01-20 19:58     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 18:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> C compilers are allowed to represent enums as a smaller type
> than int, if all enum values fit in the smaller type.  There
> are even compiler flags that force the use of this smaller
> representation, and using them changes the ABI of a binary.

Suggest "although using them".

> Therefore, our generated code for visit_type_ENUM() (for all
> qapi enums) was wrong for casting Enum* to int* when calling
> visit_type_enum().
>
> It appears that no one has been doing this for qemu, because
> if they had, we are potentially dereferencing beyond bounds
> or even risking a SIGBUS on platforms where unaligned pointer
> dereferencing is fatal.  Better is to avoid the practice
> entirely, and just use the correct types.
>
> This matches the fix for alternate qapi types, done earlier in
> commit 0426d53 "qapi: Simplify visiting of alternate types",
> with generated code changing as:
>
> | void visit_type_GuestDiskBusType(Visitor *v, GuestDiskBusType *obj, const char *name, Error **errp)
> | {
> |-    visit_type_enum(v, (int *)obj, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
> |+    int tmp = *obj;
> |+    visit_type_enum(v, &tmp, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
> |+    *obj = tmp;
> | }

Long lines.  Do we have an example with a shorter enum name handy?

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: mention earlier commit id, enhance commit message
> v8: no change
> v7: rebase on typo fix
> v6: new patch
> ---
>  scripts/qapi-visit.py | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 4a4f67d..6bd188b 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -178,12 +178,13 @@ out:
>
>
>  def gen_visit_enum(name):
> -    # FIXME cast from enum *obj to int * invalidly assumes enum is int
>      return mcgen('''
>
>  void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
>  {
> -    visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
> +    int tmp = *obj;
> +    visit_type_enum(v, &tmp, %(c_name)s_lookup, "%(name)s", name, errp);
> +    *obj = tmp;
>  }
>  ''',
>                   c_name=c_name(name), name=name)

Same pattern in qapi-visit-core.c, except we name the variable @value
there.  Your choice.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks
  2016-01-20 17:29   ` Markus Armbruster
@ 2016-01-20 18:10     ` Eric Blake
  2016-01-21  8:56       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 18:10 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 4040 bytes --]

On 01/20/2016 10:29 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Our qapi visitor contract supports multiple integer visitors,
>> but left the type_uint64 visitor as optional (falling back on
>> type_int64); it also marks the type_size visitor as optional
>> (falling back on type_uint64 or even type_int64).
>>
>> Note that the default of falling back on type_int for unsigned
>> visitors can cause confusing results for values larger than
>> INT64_MAX (such as having to pass in a negative 2s complement
>> value on input, and getting a negative result on output).
>>
>> This patch does not fully address the disparity in handling
>> large values as negatives, but does move towards a cleaner
>> situation where EVERY visitor provides both type_int64 and
>> type_uint64 variants as entry points; then each client can
>> either implement sane differences between the two, or document
>> in place with a FIXME that there is munging going on.
> 
> Before: nobody implements type_uint64(), and the core falls back to
> type_int64(), casting negative values to large positive ones.  With an
> implementation of type_int64() that parses large positive values as
> negative, the two casts cancel out.
> 
> After: everybody implements type_uint64() incorrectly, by reusing
> type_int64() code.  The problem moves from visitor core to visitor
> implementations, where we can actually fix it if we care.
> 
> Correct?

Close. opts-visitor.c already implemented type_uint64, but also has its
known bugs (and Paolo has been pinged about his claim for fixes in that
file...)

But otherwise, yes, in this patch, we are not fixing insanity so much as
localizing and better documenting it.

I've given some thought about converting the QObject 'int' type to track
a sign bit, making it effectively a superset covering 3*2^63 values in
the range [INT64_MIN, UINT64_MAX], and then enhancing the parser to
track if a negative value was parsed and the formatter to output
negative if the sign bit is set, and then make the 'int64' and 'uint64'
parsers stick to stricter 2*64 subsets of that range.  But I haven't
actually written a patch along those lines yet.

> 
>> The dealloc visitor no longer needs a type_size callback,
>> since that now safely falls back to the type_uint64 callback.
> 
> Did it need the callback before this patch?

Not really.  Should I split out the deletion of that callback as a
separate patch?

> 
>> Then, in qapi-visit-core.c, we can now use the guaranteed
>> type_uint64 callback as the fallback for all smaller unsigned
>> int visits.
> 
> The type_int64() callback works just fine for smaller unsigned ints, but
> I agree avoiding mixed signedness by using type_uint64() make sense once
> type_uint64() is mandatory.
> 

>> +++ b/qapi/qapi-visit-core.c
>> @@ -102,15 +102,15 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>>
>>  void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
>>  {
>> -    int64_t value;
>> +    uint64_t value;
>>
>>      if (v->type_uint8) {
>>          v->type_uint8(v, obj, name, errp);
>>      } else {
>>          value = *obj;
>> -        v->type_int64(v, &value, name, errp);
>> -        if (value < 0 || value > UINT8_MAX) {
>> -            /* FIXME questionable reuse of errp if type_int64() changes
>> +        v->type_uint64(v, &value, name, errp);
>> +        if (value > UINT8_MAX) {
>> +            /* FIXME questionable reuse of errp if type_uint64() changes
>>                 value on error */
>>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
>>                         name ? name : "null", "uint8_t");
> 
> You could delay adding these FIXMEs until this patch, and reduce churn.
> Probably not worth the bother now.

Yeah, I'll see how the rest of the series review goes.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks
  2016-01-20 17:34   ` Markus Armbruster
@ 2016-01-20 18:17     ` Eric Blake
  2016-01-21  9:05       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 18:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 3364 bytes --]

On 01/20/2016 10:34 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Commit 4e27e819 introduced optional visitor callbacks for all
>> sorts of int types, but no visitor has supplied any of the
>> callbacks for sizes less than 64 bits.  In other words, the
>> generic implementation based on using type_[u]int64() followed
>> by bounds-checking works just fine. In the interest of
>> simplicity, it's easier to make the visitor callback interface
>> not have to worry about the other sizes.
>>
>> Adding some helper functions minimizes the boilerplate required
>> to correct FIXMEs added earlier with regards to questionable
>> reuse of errp, particularly now that we can guarantee from a
>> single file audit that value is unchanged if an error is set.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>
>> ---
>> v9: hoist some of visitor-impl.h changes into 9/35 and 10/35
>> v8: no change
>> v7: further factor out helper functions that eliminate the
>> questionable errp reuse
>> v6: split off from v5 23/46
>> original version also appeared in v6-v9 of subset D
>> ---
>>  include/qapi/visitor-impl.h |   8 +--
>>  qapi/qapi-visit-core.c      | 158 +++++++++++++++++---------------------------
>>  2 files changed, 60 insertions(+), 106 deletions(-)
> 
> Nice diffstat.
> 
>>
>> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
>> index 45c1d3e..e6399d1 100644
>> --- a/include/qapi/visitor-impl.h
>> +++ b/include/qapi/visitor-impl.h
>> @@ -1,7 +1,7 @@
>>  /*
>>   * Core Definitions for QAPI Visitor implementations
>>   *
>> - * Copyright (C) 2012 Red Hat, Inc.
>> + * Copyright (C) 2012, 2015-2016 Red Hat, Inc.
> 
> git-log has authors @redhat.com in 2013 and 2014 as well.

I didn't bother to check whether those edits were complex enough to
warrant claiming copyright additions; but I'm also amenable to
shortening to '2012-2016' on a respin regardless of the sizes of those
edits.  As it is, I was not very careful about adding 2016 in the v9
spin, so I may be inconsistent on which years are claimed where, in
comparison to where I felt I was adding new copyrightable content rather
than just minor fixing of existing content.

[While it's nice that we DON'T have to assign copyright to a central
organization to contribute to qemu, sometimes it is much nicer working
on FSF code where a single copyright holder makes discussions like this
moot.]

>> +    } else if (value > max) {
>> +        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
>> +                   name ? name : "null", type);
> 
> We should clean up this name ? name : "null" nonsense some day.

It all stems from whether the visitor is locally visiting { 'name':value
} vs. [ value ]; in an array visit, there is no direct name.

Maybe we could go a step higher; if we have:

{ 'name': [ value ] }

then we could require callers to parse value by passing in "[name]" or
"name[0]" rather than NULL.  Then audit the code to always pass in a
sensible name at the top level of a parse.  It would even extend to 2-D
arrays, via "[[name]]" or "name[0][0]".  But not in this series.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement Eric Blake
@ 2016-01-20 18:28   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 18:28 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Richard Henderson, Eduardo Habkost,
	open list:Block layer core, Michael S. Tsirkin, John Snow,
	Jason Wang, qemu-devel, Alexander Graf, Keith Busch,
	marcandre.lureau, Gonglei, open list:sPAPR, Gerd Hoffmann,
	Igor Mammedov, Paolo Bonzini, Luiz Capitulino, Jiri Slaby,
	Michael Roth, Andreas Färber, David Gibson

Eric Blake <eblake@redhat.com> writes:

> JSON uses "name":value, but many of our visitor interfaces were
> called with visit_type_FOO(v, &value, name, errp).  This can be
> a bit confusing to have to mentally swap the parameter order to
> match JSON order.  It's particularly bad for visit_start_struct(),
> where the 'name' parameter is smack in the middle of the
> otherwise-related group of 'obj, kind, size' parameters! It's
> time to do a global swap of the parameter ordering, so that the
> 'name' parameter is always immediately after the Visitor argument.

Yes, please!

> Additional reasons in favor of the swap: name is always an input
> parameter, while &value is sometimes an output parameter (depending
> on whether the caller is using an input visitor); and it is nicer
> to list input parameters first.

Except when it isn't: memcpy(), strcat(), ...  I'd scratch this
argument.  The case for the transformation is plenty strong without it.


>                                  Also, the existing include/qjson.h
> prefers listing 'name' first in json_prop_*(), and I have plans to
> unify that file with the qapi visitors; listing 'name' first in
> qapi will minimize churn to the (admittedly few) qjson.h clients.
>
> The next patches will then fix docs, object.h, visitor-impl.h, and
> those clients to match.
>
> Done by first patching scripts/qapi*.py by hand to make generated
> files do what I want, then by running the following Coccinelle
> script to affect the rest of the code base:
>  $ spatch --sp-file script `git grep -l '\bvisit_' -- '**/*.[ch]'`
> I then had to apply some touchups (Coccinelle insisted on TAB
> indentation in visitor.h, and botched the signature of
> visit_type_enum() by rewriting 'const char *const strings[]' to
> the syntactically invalid 'const char*const[] strings').
>
>     // Part 1: Swap declaration order
>     @@
>     type TV, TErr, TObj, T1, T2;
>     identifier OBJ, ARG1, ARG2;
>     @@
>      void visit_start_struct
>     -(TV v, TObj OBJ, T1 ARG1, const char *name, T2 ARG2, TErr errp)
>     +(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)

Rotates second to fourth parameters right.  Their types are sufficiently
incompatible, so the compiler should catch inconsistencies.

Similar argument for the other functions.

>      { ... }
>
>     @@
>     type bool, TV, T1;
>     identifier ARG1;
>     @@
>      bool visit_optional
>     -(TV v, T1 ARG1, const char *name)
>     +(TV v, const char *name, T1 ARG1)
>      { ... }
>
>     @@
>     type TV, TErr, TObj, T1;
>     identifier OBJ, ARG1;
>     @@
>      void visit_get_next_type
>     -(TV v, TObj OBJ, T1 ARG1, const char *name, TErr errp)
>     +(TV v, const char *name, TObj OBJ, T1 ARG1, TErr errp)
>      { ... }
>
>     @@
>     type TV, TErr, TObj, T1, T2;
>     identifier OBJ, ARG1, ARG2;
>     @@
>      void visit_type_enum
>     -(TV v, TObj OBJ, T1 ARG1, T2 ARG2, const char *name, TErr errp)
>     +(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
>      { ... }
>
>     @@
>     type TV, TErr, TObj;
>     identifier OBJ;
>     identifier VISIT_TYPE =~ "^visit_type_";
>     @@
>      void VISIT_TYPE
>     -(TV v, TObj OBJ, const char *name, TErr errp)
>     +(TV v, const char *name, TObj OBJ, TErr errp)
>      { ... }
>
>     // Part 2: swap caller order
>     @@
>     expression V, NAME, OBJ, ARG1, ARG2, ERR;
>     identifier VISIT_TYPE =~ "^visit_type_";
>     @@
>     (
>     -visit_start_struct(V, OBJ, ARG1, NAME, ARG2, ERR)
>     +visit_start_struct(V, NAME, OBJ, ARG1, ARG2, ERR)
>     |
>     -visit_optional(V, ARG1, NAME)
>     +visit_optional(V, NAME, ARG1)
>     |
>     -visit_get_next_type(V, OBJ, ARG1, NAME, ERR)
>     +visit_get_next_type(V, NAME, OBJ, ARG1, ERR)
>     |
>     -visit_type_enum(V, OBJ, ARG1, ARG2, NAME, ERR)
>     +visit_type_enum(V, NAME, OBJ, ARG1, ARG2, ERR)
>     |
>     -VISIT_TYPE(V, OBJ, NAME, ERR)
>     +VISIT_TYPE(V, NAME, OBJ, ERR)
>     )
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Didn't check the patch closely.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor Eric Blake
@ 2016-01-20 18:49   ` Markus Armbruster
  2016-01-20 20:54     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 18:49 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Eduardo Habkost, open list:nvme, Michael S. Tsirkin,
	John Snow, Jason Wang, qemu-devel, Alexander Graf, Keith Busch,
	marcandre.lureau, Gonglei, open list:sPAPR, Gerd Hoffmann,
	Igor Mammedov, Paolo Bonzini, Richard Henderson, Jiri Slaby,
	Andreas Färber, David Gibson

Eric Blake <eblake@redhat.com> writes:

> Similar to the previous patch, it's nice to have all functions
> in the tree that involve a visitor and a name for conversion to
> or from QAPI to consistently stick the 'name' parameter next
> to the Visitor parameter.
>
> Done by manually changing include/qom/object.h and qom/object.c,
> then running this Coccinelle script and touching up the fallout
> (Coccinelle insisted on adding some trailing whitespace).
>
>     @ rule1 @
>     identifier fn;
>     type Object, Visitor, Error;
>     identifier obj, v, opaque, name, errp;
>     @@
>      void fn
>     - (Object *obj, Visitor *v, void *opaque, const char *name,
>     + (Object *obj, Visitor *v, const char *name, void *opaque,
>        Error **errp) { ... }

I think we want to match void functions with exactly these parameter
types.  The parameter names don't matter.

However, the actual match is looser!  For instance, it also matches

    void foo(int *pi, unsigned *pu, void *vp, const char *cp, double **dpp)
    {
    }

This could mess up unrelated function.  I could double-check it doesn't,
but I'd rather have a narrower match instead.  Can't give one offhand,
though.  Ideas?

>
>     @@
>     identifier rule1.fn;
>     expression obj, v, opaque, name, errp;
>     @@
>      fn(obj, v,
>     -   opaque, name,
>     +   name, opaque,
>         errp)

The rule1.fn restricts the match to functions changed by the previous
rule.  Good.

>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API Eric Blake
@ 2016-01-20 18:55   ` Markus Armbruster
  2016-01-20 21:01     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 18:55 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> As explained in the previous patches, matching argument order of
> 'name, &value' to JSON's "name":value makes sense.  However,
> while the last two patches were easy with Coccinelle, I ended up
> doing this one all by hand.  Now all the visitor callbacks match
> the main interface.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: new patch
> ---
>  include/qapi/visitor-impl.h  | 39 +++++++++++++++++++++------------------
>  qapi/opts-visitor.c          | 16 ++++++++--------
>  qapi/qapi-dealloc-visitor.c  | 25 ++++++++++++-------------
>  qapi/qapi-visit-core.c       | 38 +++++++++++++++++++-------------------
>  qapi/qmp-input-visitor.c     | 22 +++++++++++-----------
>  qapi/qmp-output-visitor.c    | 16 ++++++++--------
>  qapi/string-input-visitor.c  | 16 ++++++++--------
>  qapi/string-output-visitor.c | 16 ++++++++--------
>  8 files changed, 95 insertions(+), 93 deletions(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index e6399d1..0257359 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -18,8 +18,8 @@
>  struct Visitor
>  {
>      /* Must be set */
> -    void (*start_struct)(Visitor *v, void **obj, const char *kind,
> -                         const char *name, size_t size, Error **errp);
> +    void (*start_struct)(Visitor *v, const char *name, void **obj,
> +                         const char *kind, size_t size, Error **errp);
>      void (*end_struct)(Visitor *v, Error **errp);
>
>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
> @@ -30,39 +30,42 @@ struct Visitor
>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>      void (*end_list)(Visitor *v, Error **errp);
>
> -    void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
> -                      const char *kind, const char *name, Error **errp);
> +    void (*type_enum)(Visitor *v, const char *name, int *obj,
> +                      const char * const strings[], const char *kind,

Opportunity to change to 'const char *const'.  I prefer that, because it
makes the fact that this is a pointer-* and not a binary operator-*
visually obvious.

Same elsewhere.

> +                      Error **errp);
>      /* May be NULL; only needed for input visitors. */
> -    void (*get_next_type)(Visitor *v, QType *type, bool promote_int,
> -                          const char *name, Error **errp);
> +    void (*get_next_type)(Visitor *v, const char *name, QType *type,
> +                          bool promote_int, Error **errp);
>
>      /* Must be set. */
> -    void (*type_int64)(Visitor *v, int64_t *obj, const char *name,
> +    void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
>                         Error **errp);
>      /* Must be set. */
> -    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name,
> +    void (*type_uint64)(Visitor *v, const char *name, uint64_t *obj,
>                          Error **errp);
>      /* Optional; fallback is type_uint64().  */
> -    void (*type_size)(Visitor *v, uint64_t *obj, const char *name,
> +    void (*type_size)(Visitor *v, const char *name, uint64_t *obj,
>                        Error **errp);
>      /* Must be set. */
> -    void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
> -    void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
> -    void (*type_number)(Visitor *v, double *obj, const char *name,
> +    void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp);
> +    void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp);
> +    void (*type_number)(Visitor *v, const char *name, double *obj,
>                          Error **errp);
> -    void (*type_any)(Visitor *v, QObject **obj, const char *name,
> +    void (*type_any)(Visitor *v, const char *name, QObject **obj,
>                       Error **errp);
>
>      /* May be NULL; most useful for input visitors. */
> -    void (*optional)(Visitor *v, bool *present, const char *name);
> +    void (*optional)(Visitor *v, const char *name, bool *present);
>
>      bool (*start_union)(Visitor *v, bool data_present, Error **errp);
>      void (*end_union)(Visitor *v, bool data_present, Error **errp);
>  };
>
> -void input_type_enum(Visitor *v, int *obj, const char * const strings[],
> -                     const char *kind, const char *name, Error **errp);
> -void output_type_enum(Visitor *v, int *obj, const char * const strings[],
> -                      const char *kind, const char *name, Error **errp);
> +void input_type_enum(Visitor *v, const char *name, int *obj,
> +                     const char * const strings[], const char *kind,
> +                     Error **errp);
> +void output_type_enum(Visitor *v, const char *name, int *obj,
> +                      const char * const strings[], const char *kind,
> +                      Error **errp);
>
>  #endif

I checked the changes to this file carefully.  Can we rely on the
compiler to flag mistakes in the rest of the patch?

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
@ 2016-01-20 18:59   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 18:59 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Michael S. Tsirkin, Alexander Graf, qemu-devel,
	open list:sPAPR, marcandre.lureau, Paolo Bonzini,
	Luiz Capitulino, Andreas Färber, David Gibson

Eric Blake <eblake@redhat.com> writes:

> visit_start_struct() and visit_type_enum() had a 'kind' argument
> that was usually set to either the stringized version of the
> corresponding qapi type name, or to NULL (although some clients
> didn't even get that right).  But nothing ever used the argument.
> It's even hard to argue that it would be useful in a debugger,
> as a stack backtrace also tells which type is being visited.
>
> Therefore, drop the 'kind' argument as dead.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to 'name' motion
> v7: new patch
> ---
>  hmp.c                       |  2 +-
>  hw/core/qdev-properties.c   |  6 ++----
>  hw/ppc/spapr_drc.c          |  4 ++--
>  hw/virtio/virtio-balloon.c  |  4 ++--
>  include/qapi/visitor-impl.h | 11 ++++-------
>  include/qapi/visitor.h      |  5 ++---
>  qapi/opts-visitor.c         |  2 +-
>  qapi/qapi-dealloc-visitor.c |  6 ++----
>  qapi/qapi-visit-core.c      | 16 +++++++---------
>  qapi/qmp-input-visitor.c    |  2 +-
>  qapi/qmp-output-visitor.c   |  3 +--
>  qom/object.c                |  8 ++++----
>  scripts/qapi-event.py       |  2 +-
>  scripts/qapi-visit.py       | 12 ++++++------
>  vl.c                        |  2 +-
>  15 files changed, 37 insertions(+), 48 deletions(-)
[...]
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 0257359..6abfda7 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -19,7 +19,7 @@ struct Visitor
>  {
>      /* Must be set */
>      void (*start_struct)(Visitor *v, const char *name, void **obj,
> -                         const char *kind, size_t size, Error **errp);
> +                         size_t size, Error **errp);
>      void (*end_struct)(Visitor *v, Error **errp);
>
>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
> @@ -31,8 +31,7 @@ struct Visitor
>      void (*end_list)(Visitor *v, Error **errp);
>
>      void (*type_enum)(Visitor *v, const char *name, int *obj,
> -                      const char * const strings[], const char *kind,
> -                      Error **errp);
> +                      const char *const strings[], Error **errp);
>      /* May be NULL; only needed for input visitors. */
>      void (*get_next_type)(Visitor *v, const char *name, QType *type,
>                            bool promote_int, Error **errp);
> @@ -62,10 +61,8 @@ struct Visitor
>  };
>
>  void input_type_enum(Visitor *v, const char *name, int *obj,
> -                     const char * const strings[], const char *kind,
> -                     Error **errp);
> +                     const char *const strings[], Error **errp);
>  void output_type_enum(Visitor *v, const char *name, int *obj,
> -                      const char * const strings[], const char *kind,
> -                      Error **errp);
> +                      const char *const strings[], Error **errp);
>
>  #endif
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index eb50116..4abc180 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -28,7 +28,7 @@ typedef struct GenericList
>  } GenericList;
>
>  void visit_start_struct(Visitor *v, const char *name, void **obj,
> -                        const char *kind, size_t size, Error **errp);
> +                        size_t size, Error **errp);
>  void visit_end_struct(Visitor *v, Error **errp);
>  void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
>                                   Error **errp);
> @@ -54,8 +54,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
>  void visit_get_next_type(Visitor *v, const char *name, QType *type,
>                           bool promote_int, Error **errp);
>  void visit_type_enum(Visitor *v, const char *name, int *obj,
> -                     const char *const strings[], const char *kind,
> -                     Error **errp);
> +                     const char *const strings[], Error **errp);
>  void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
>  void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
>                        Error **errp);

Looks good.  We should be able to rely on the compiler to check the
changes to callers.

[...]
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 7bc3de6..b0452cf 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -2,6 +2,7 @@
>   * Core Definitions for QAPI Visitor Classes
>   *
>   * Copyright IBM, Corp. 2011
> + * Copyright (C) 2015 Red Hat, Inc.

2012-2015 would be more accurate.

>   *
>   * Authors:
>   *  Anthony Liguori   <aliguori@us.ibm.com>
[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct Eric Blake
@ 2016-01-20 19:03   ` Markus Armbruster
  2016-01-20 21:58     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-20 19:03 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Alexander Graf, qemu-devel, open list:sPAPR,
	marcandre.lureau, David Gibson

Eric Blake <eblake@redhat.com> writes:

> No backend was setting an error when ending the visit of a list
> or implicit struct.

That's a lie: qmp_input_end_list() does.  But it shouldn't, as you
explain below.  Rephrase the commit message?

>                      Make the callers a bit easier to follow by
> making this a part of the contract, and removing the errp
> argument - callers can then unconditionally end an object as
> part of cleanup without having to think about whether a second
> error is dominated by a first, because there is no second error.
>
> The only addition of &error_abort in this patch, in the function
> qmp_input_end_list(), will never trigger unless a programming
> bug creates a push(struct)/pop(list) or push(list)/pop(struct)
> mismatch.
>
> A later patch will then tackle the larger task of splitting
> visit_end_struct(), which can indeed set an error (and that
> cleanup will also have the side-effect of removing the use of
> error_abort added here).
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Patch looks good.  I like the simplification.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int*
  2016-01-20 18:08   ` Markus Armbruster
@ 2016-01-20 19:58     ` Eric Blake
  2016-01-21  9:08       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 19:58 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1590 bytes --]

On 01/20/2016 11:08 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> C compilers are allowed to represent enums as a smaller type
>> than int, if all enum values fit in the smaller type.  There
>> are even compiler flags that force the use of this smaller
>> representation, and using them changes the ABI of a binary.
> 
> Suggest "although using them".
> 

Okay.


>> with generated code changing as:
>>
>> | void visit_type_GuestDiskBusType(Visitor *v, GuestDiskBusType *obj, const char *name, Error **errp)
>> | {
>> |-    visit_type_enum(v, (int *)obj, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
>> |+    int tmp = *obj;
>> |+    visit_type_enum(v, &tmp, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
>> |+    *obj = tmp;
>> | }
> 
> Long lines.  Do we have an example with a shorter enum name handy?

Shortest is QType; runner-ups RxState and TpmType.


>>  void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
>>  {
>> -    visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
>> +    int tmp = *obj;
>> +    visit_type_enum(v, &tmp, %(c_name)s_lookup, "%(name)s", name, errp);
>> +    *obj = tmp;
>>  }
>>  ''',
>>                   c_name=c_name(name), name=name)
> 
> Same pattern in qapi-visit-core.c, except we name the variable @value
> there.  Your choice.

'value' sounds consistent. An easy swap on a respin.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  2016-01-20 18:49   ` Markus Armbruster
@ 2016-01-20 20:54     ` Eric Blake
  2016-01-21  9:18       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 20:54 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Eduardo Habkost, open list:nvme, Michael S. Tsirkin,
	John Snow, Jason Wang, qemu-devel, Alexander Graf, Keith Busch,
	marcandre.lureau, Gonglei, open list:sPAPR, Gerd Hoffmann,
	Igor Mammedov, Paolo Bonzini, Richard Henderson, Jiri Slaby,
	Andreas Färber, David Gibson

[-- Attachment #1: Type: text/plain, Size: 2978 bytes --]

On 01/20/2016 11:49 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Similar to the previous patch, it's nice to have all functions
>> in the tree that involve a visitor and a name for conversion to
>> or from QAPI to consistently stick the 'name' parameter next
>> to the Visitor parameter.
>>
>> Done by manually changing include/qom/object.h and qom/object.c,
>> then running this Coccinelle script and touching up the fallout
>> (Coccinelle insisted on adding some trailing whitespace).
>>
>>     @ rule1 @
>>     identifier fn;
>>     type Object, Visitor, Error;
>>     identifier obj, v, opaque, name, errp;
>>     @@
>>      void fn
>>     - (Object *obj, Visitor *v, void *opaque, const char *name,
>>     + (Object *obj, Visitor *v, const char *name, void *opaque,
>>        Error **errp) { ... }
> 
> I think we want to match void functions with exactly these parameter
> types.  The parameter names don't matter.

The parameter names shouldn't matter; the 'identifier obj' should have
been enough to make 'obj' a metavariable matching any actual parameter name.

> 
> However, the actual match is looser!  For instance, it also matches
> 
>     void foo(int *pi, unsigned *pu, void *vp, const char *cp, double **dpp)
>     {
>     }

Uggh. My intent was to match exactly 'Object *' and 'Visitor *' as the
first two types, where 'int *' and 'unsigned *' are NOT matches.  But I
don't know Coccinelle well enough to make that blatantly obvious (is my
declaration of 'type Object' not correct?).

> 
> This could mess up unrelated function.  I could double-check it doesn't,
> but I'd rather have a narrower match instead.  Can't give one offhand,
> though.  Ideas?

Is 'typedef' better than 'type' for constraining the type of the first
two arguments?  Or does Coccinelle do literal matches on anything you
don't pre-declare, as in:

     @ rule1 @
     identifier fn;
     identifier obj, v, opaque, name, errp;
     @@
      void fn
     - (Object *obj, Visitor *v, void *opaque, const char *name,
     + (Object *obj, Visitor *v, const char *name, void *opaque,
        Error **errp) { ... }


Fortunately, a manual inspection of the results (which I had to do
anyways due to spacing issues) didn't spot any incorrect swaps.

At this point, I don't know that re-writing Coccinelle will be worth the
hassle (nothing else needs to be rewritten).

> 
>>
>>     @@
>>     identifier rule1.fn;
>>     expression obj, v, opaque, name, errp;
>>     @@
>>      fn(obj, v,
>>     -   opaque, name,
>>     +   name, opaque,
>>         errp)
> 
> The rule1.fn restricts the match to functions changed by the previous
> rule.  Good.
> 
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API
  2016-01-20 18:55   ` Markus Armbruster
@ 2016-01-20 21:01     ` Eric Blake
  2016-01-21  9:19       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 21:01 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 2037 bytes --]

On 01/20/2016 11:55 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> As explained in the previous patches, matching argument order of
>> 'name, &value' to JSON's "name":value makes sense.  However,
>> while the last two patches were easy with Coccinelle, I ended up
>> doing this one all by hand.  Now all the visitor callbacks match
>> the main interface.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>

>> @@ -30,39 +30,42 @@ struct Visitor
>>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>>      void (*end_list)(Visitor *v, Error **errp);
>>
>> -    void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
>> -                      const char *kind, const char *name, Error **errp);
>> +    void (*type_enum)(Visitor *v, const char *name, int *obj,
>> +                      const char * const strings[], const char *kind,
> 
> Opportunity to change to 'const char *const'.  I prefer that, because it
> makes the fact that this is a pointer-* and not a binary operator-*
> visually obvious.
> 
> Same elsewhere.

Hmm, I probably have churn later in the series.  Will fix.

>>      /* May be NULL; most useful for input visitors. */
>> -    void (*optional)(Visitor *v, bool *present, const char *name);
>> +    void (*optional)(Visitor *v, const char *name, bool *present);
>>

> 
> I checked the changes to this file carefully.  Can we rely on the
> compiler to flag mistakes in the rest of the patch?

C's (intentionally-loose) treatment of 'char *' like 'void *' is a bit
worrisome, but the fact that we have 'const' on only one of the two
swapped arguments was indeed enough to make the compiler complain about
mismatch in parameter types when trying to assign incorrectly-typed
static functions to the updated struct members.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct
  2016-01-20 19:03   ` Markus Armbruster
@ 2016-01-20 21:58     ` Eric Blake
  2016-01-21  9:47       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-20 21:58 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Michael Roth, Alexander Graf, qemu-devel, open list:sPAPR,
	marcandre.lureau, David Gibson

[-- Attachment #1: Type: text/plain, Size: 3119 bytes --]

On 01/20/2016 12:03 PM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> No backend was setting an error when ending the visit of a list
>> or implicit struct.
> 
> That's a lie: qmp_input_end_list() does.  But it shouldn't, as you
> explain below.  Rephrase the commit message?

I'm not sure why you call it a lie - qmp_input_end_list() will not set
an error unless it is mistakenly paired with a push(struct), which none
of our code base does.  Or put another way, although qmp_input_pop()
[called by qmp_input_end_list()] has a signature that can set an error,
closer inspection shows that it will only do so when invoked to close
out a struct, and not when closing out a list.  But that's a blatant
programmer mismatch, which none of our code base does, so no well-formed
use of visitors can cause qmp_input_end_list() to set an error.

> 
>>                      Make the callers a bit easier to follow by
>> making this a part of the contract, and removing the errp
>> argument - callers can then unconditionally end an object as
>> part of cleanup without having to think about whether a second
>> error is dominated by a first, because there is no second error.
>>
>> The only addition of &error_abort in this patch, in the function
>> qmp_input_end_list(), will never trigger unless a programming
>> bug creates a push(struct)/pop(list) or push(list)/pop(struct)
>> mismatch.

I'm open to wording suggestions.

Maybe replace all of the above with:

None of the existing .end_implicit_struct() implementations use errp.
And of the existing .end_list() implementations, only
qmp_input_end_list() even uses errp, but closer inspection shows that it
will never be modified (errp is only passed to qmp_input_pop(), which
will only set an error if the corresponding push was a struct rather
than a list).  We can turn that internal usage into an &error_abort, to
protect against programmer mistakes of push(struct)/pop(list) or
push(list)/pop(struct) mismatch.

With that done, we can then make all public uses of
visit_end_implicit_struct() and visit_end_list() easier to follow by
removing the errp argument and making error-free operation part of the
contract.  Callers can then unconditionally end an object as part of
cleanup without having to think about whether a second error is
dominated by a first, because there is no possibility of a second error.

>>
>> A later patch will then tackle the larger task of splitting
>> visit_end_struct(), which can indeed set an error (and that
>> cleanup will also have the side-effect of removing the use of
>> error_abort added here).
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Patch looks good.  I like the simplification.

Would help to split this into two patches, one switching from
qmp_input_pop(errp) into qmp_input_pop(&error_abort), and the other then
removing unused errp argument?

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling
  2016-01-20 15:55     ` Eric Blake
@ 2016-01-21  6:21       ` Markus Armbruster
  2016-01-21 17:12         ` Eric Blake
  2016-01-21 17:29         ` Daniel P. Berrange
  0 siblings, 2 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  6:21 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 02:02 AM, Markus Armbruster wrote:
>
>>> @@ -519,6 +519,8 @@ static QObject *parse_literal(JSONParserContext *ctxt)
>>>      }
>>>      case JSON_FLOAT:
>>>          /* FIXME dependent on locale */
>>> +        /* FIXME our lexer matches RFC7159 in forbidding Inf or NaN,
>> 
>> For what it's worth, the RFC spells this "RFC 7159".
>
> Looks like we use space more often than not, but that we're
> inconsistent.  For example:
>
> slirp/tcp.h: * Per RFC 793, September, 1981.
> slirp/tcp.h: * Per RFC793, September, 1981.
>
> Will fix if I need to respin, otherwise I assume you can do it.

Okay.

>>> +        /* FIXME: snprintf() is locale dependent; but JSON requires
>>> +         * numbers to be formatted as if in the C locale. */
>> 
>> The new FIXME matches the existing one in parse_literal(), visible
>> above.
>> 
>> However, dependence on C locale is a pervasive issue in QEMU.  These two
>> comments could give readers the idea that it's an issue just here.
>> Suggest to add something like "Dependence on C locale is a pervasive
>> issue in QEMU."
>
> Good idea.
>
>> 
>>> +        /* FIXME: This risks printing Inf or NaN, which are not valid
>>> +         * JSON values. */
>>> +        /* FIXME: the default precision of %f may be insufficient to
>>> +         * tell this number apart from others. */
>> 
>> Yup.
>> 
>> The easy way to avoid loss of precision is %a, but of course that's way
>> too much sophistication for JSON.
>> 
>> Avoiding loss of precision with a decimal format is non-trivial; see
>> Steele, Jr., Guy L., and White, Jon L. How to print floating-point
>> numbers accurately, SIGPLAN ’90, and later improvements collected here:
>> http://stackoverflow.com/questions/7153979/algorithm-to-convert-an-ieee-754-double-to-a-string
>
> Ah, memories.  I read and implemented that paper when working on the
> jikes compiler for the Java language back in the late nineties, as it is
> the Java language specification which had the very neat property of
> requiring the shortest decimal string that will unambiguously round back
> to the same floating point pattern.
>
> One alternative is to always output a guaranteed unambiguous decimal
> string (although not necessarily the shortest), by using %.17f, using
> <float.h> DBL_DECIMAL_DIG.  (Note that DBL_DIG of 15 is NOT sufficient -
> it is the lower limit that says that a decimal->float->decimal will not
> change the decimal; but we want the converse where a
> float->decimal->float will not change the float.  There are stretches of
> numbers where the pigeonhole principle applies; you can think of it this
> way: there is no way to map all possible 2^10 (1024) binary values
> inside 2^3 (1000) decimal digits without at least 24 of them needing one
> more decimal digit.  But by the same arguments, DBL_DECIMAL_DIG is an
> upper limit and usually more than you need.)
>
> So, the question is whether we want to always output 17 digits, or
> whether we want to do the poor-man's truncation scheme (easy to
> understand, but not optimal use of the processor), or go all the way to
> the algorithm of that paper (faster but lots harder to understand).  For
> reference, here's the poor-man's algorithm in pseudocode:

I don't think we want to implement floating-point formatting ourselves.

> if 0, inf, nan:
>     special case
> else:
>     Obtain the DBL_DECIMAL_DIG string via sprintf %.17f
>     i = 17;
>     do {
>         truncate the original string to i-1 decimal characters
>         parse that with strtod()
>         if the bit pattern differs:
>             break;
>     } while (--i);
>     assert(i)
>     use i digits of the string

That's a lot of strtod()...  May not be noticable if we write the result
to a slowish sink.  Binary search could save a few.

Naive idea: chop off trailing '0'?

> As a separate patch, of course, but I have a pending patch that provides
> a single place where we could drop in such an improvement:
> https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg03932.html

Definitely separate.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST()
  2016-01-20 15:59     ` Eric Blake
@ 2016-01-21  6:22       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  6:22 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 03:04 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> The macro DO_UPCAST() is incorrectly named: it converts from a
>>> parent class to a derived class (which is a downcast).  Better,
>>> and more consistent with some of the other qapi visitors, is
>>> to use the container_of() macro through a to_FOO() helper.
>>>
>>> Our current definition of container_of() is weaker than
>>> DO_UPCAST(), in that it does not require the derived class to
>>> have Visitor as its first member, but this does not hurt our
>>> usage patterns in qapi visitors.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>>
>
>>>
>>> +static OptsVisitor *to_ov(Visitor *v)
>>> +{
>>> +    return container_of(v, OptsVisitor, visitor);
>>> +}
>>> +
>>> +
>> 
>> The name to_ov() is rather laconic even for my taste.  Tolerable, since
>> it's static.
>
> And matches existing practice pre-patch:
>
> qapi/qapi-dealloc-visitor.c:static QapiDeallocVisitor *to_qov(Visitor *v)
> qapi/qmp-input-visitor.c:static QmpInputVisitor *to_qiv(Visitor *v)
> qapi/qmp-output-visitor.c:static QmpOutputVisitor *to_qov(Visitor *v)
>
>
>> 
>> Patch gets rid of all DO_UPCAST() in qapi/.  PATCH 33 brings one back;
>> it should use to_ov() instead.
>
> D'oh. You can tell what order I wrote the patches in :)
>
> If I need to respin, I'll fix it; otherwise, I assume you can handle it.

Yes.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 05/37] vl: Improve use of qapi visitor
  2016-01-20 16:18     ` Eric Blake
@ 2016-01-21  6:45       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  6:45 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Paolo Bonzini

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 06:43 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Cache the visitor in a local variable instead of repeatedly
>>> calling the accessor.  Pass NULL for the visit_start_struct()
>>> object (which matches the fact that we were already passing 0
>>> for the size argument, because we aren't using the visit to
>>> allocate a qapi struct).  Guarantee that visit_end_struct()
>>> is called if visit_start_struct() succeeded.
>> 
>> Three separate things --- you're pushing it now :)
>
> Two of them the same as in 4/37.
>
> So, among the five items, I did a split in two based on file.  I could
> split in the other direction:
>
> fix hmp and vl to cache their visitor
> fix hmp and vl to pass NULL to avoid pointless allocation
> fix vl to guarantee visit_end_struct
>
>> 
>> Impact of not calling visit_end_struct()?
>
> Assertion failures after patch 24.  :)
> Basically, I wanted to see whether the code base could get simpler if we
> always enforced visit start/end grouping, and there were only a couple
> of outliers that needed fixing (here, and in patch 7).

Makes sense, although I wonder why things work without
visit_end_struct().  Looking... I guess we skip visit_end_struct() only
on error, and then we go straight to opts_visitor_cleanup().  Okay,
that's sane enough to work.

But now I wonder whether requiring visit_end_struct() before
visit_cleanup() is a good idea.

>>>
>>>      qdict_del(pdict, "qom-type");
>>> -    visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
>>> +    visit_type_str(v, &type, "qom-type", &err);
>>>      if (err) {
>>>          goto out;
>>>      }
>> 
>> If we get here, we must call visit_end_struct().
>> 
>>>      if (!type_predicate(type)) {
>>> +        visit_end_struct(v, NULL);
>> 
>> Here, you add the previously missing visit_end_struct() to the error
>> path.
>> 
>>>          goto out;
>>>      }
>>>
>>>      qdict_del(pdict, "id");
>>> -    visit_type_str(opts_get_visitor(ov), &id, "id", &err);
>>> +    visit_type_str(v, &id, "id", &err);
>>>      if (err) {
>>> -        goto out;
>>> +        goto out_end;
>> 
>> Here, you skip to the function's cleanup, which now includes
>> visit_end_struct().
>> 
>> Such a mix is can be a sign the cleanup isn't quite in the right order.
>> 
>> When we take this error path to out_end, then:
>> 
>>    out_end:
>>        visit_end_struct(v, &err_end);   // called, as we must
>>        if (!err && err_end) {           // !err is false
>>            qmp_object_del(id, NULL);    // not called
>>        }
>>        error_propagate(&err, err_end);  // since err, this is
>>                                         // error_free(err_end)
>
> Correct. And it gets simpler later on, when visit_end_struct() loses the
> errp argument (patch 33).
>
>> 
>>>      }
>>>
>>> -    object_add(type, id, pdict, opts_get_visitor(ov), &err);
>>> -    if (err) {
>>> -        goto out;
>>> -    }
>>> -    visit_end_struct(opts_get_visitor(ov), &err);
>>> -    if (err) {
>>> +    object_add(type, id, pdict, v, &err);
>>> +
>>> +out_end:
>>> +    visit_end_struct(v, &err_end);
>> 
>> visit_end_struct() must be called when visit_start_struct() succeeded.
>> All error paths after that success pass through here, except for one,
>> and that one calls visit_end_struct().  Okay.
>> 
>>> +    if (!err && err_end) {
>>>          qmp_object_del(id, NULL);
>>>      }
>> 
>> qmp_object_del() must be called when we fail after object_add()
>> succeeded.  That's what the condition does.
>> 
>>> +    error_propagate(&err, err_end);
>>>
>>>  out:
>> 
>> Cleanup below here must be done always.
>> 
>>>      opts_visitor_cleanup(ov);
>> 
>> The only reason I'm not asking you to rewrite this mess is that you're
>> already rewriting it later in this series.
>
> So I think you found patch 33.
>
>> 
>>> @@ -2867,7 +2870,6 @@ out:
>>>      QDECREF(pdict);
>>>      g_free(id);
>>>      g_free(type);
>>> -    g_free(dummy);
>>>      if (err) {
>>>          error_report_err(err);
>>>          return -1;
>> 
>> I wonder whether splitting this and the previous patch along the change
>> rather than the file would come out nicer:
>> 
>> 1. Cache the visitor
>> 
>> 2. Suppress the pointless allocation
>> 
>> 3. Fix to call visit_end_struct()
>> 
>
> What do you know - we're thinking on the same lines :)  [And now you
> know I just replied to your email in the order that I read it, rather
> than reading the whole thing first]

I do that all the time for long messages :)

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event use of qapi visitor
  2016-01-20 17:10     ` Eric Blake
@ 2016-01-21  7:16       ` Markus Armbruster
  2016-01-26 23:40       ` Eric Blake
  1 sibling, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  7:16 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 08:19 AM, Markus Armbruster wrote:
>> 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.
>> 
>> Disgusting, isn't it?  :)
>
> This, along with the fix in 5/37, were the two places that had to be
> fixed to avoid assertions in patch 24, when I turned on stricter
> enforcing of cleanup only on an evenly matched 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).
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>>> ---
>>> 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.
>
> Dead sentence leftover from v8; as mentioned above, I DID sink the
> declaration reordering to a later series for v9.  But it's after the
> ---, so it gets trimmed automatically by 'git am'.
>
>
>>>          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')
>> 
>> On error, we now go to out_obj rather than out.  Some fields will be
>> unvisited then (possibly all), and err will be set.
>> 
>> Now I get to figure out what this change changes.
>> 
>>>          ret += mcgen('''
>>> +out_obj:
>>>      visit_end_struct(v, &err);
>>>      if (err) {
>>>          goto out;
>>>      }
>> 
>> Good: we actually call visit_end_struct() as we should.
>> 
>> Not so good: if we get here via the error exit of gen_visit_fields(),
>> err is set.  If visit_end_struct() tries to set another error...
>
> Oops. It all gets cleaned up in 33 when visit_end_struct() loses the
> errp argument, but in the meantime, I think the most robust way to write
> this would be:
>
> out_obj:
>     visit_end_struct(v, err ? NULL : &err);
>     if (err) {
> ...

Works.  I don't like it much, because it doesn't conform to the usual
error handling patterns, but since it's only temporary, I'm fine with
it.

>> I guess the idea is to go from gen_visit_fields() failure through
>> visit_end_struct() here to out.  Correct?
>
> Yes.

Rather complex control flow.  At the end of the series, it looks like
this:

        visit_start_struct(v, "%(name)s", NULL, 0, &err);
        if (err) {
            goto out;
        }
        [visit the fields, on error goto out_obj...]
        visit_check_struct(v, &err);
    out_obj:
        visit_end_struct(v);
        if (err) {
            goto out;
        }

The error check after visit_end_struct() sees either an error from a
field visit, or from the visit_end_struct().

Tolerable.

>>> +++ 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'):
>> 
>> Probably clearer than label=None, but duplicates gen_err_check()'s
>> default.  Fine with me.
>> 
>>>      ret = ''
>>>      if skiperr:
>>>          errparg = 'NULL'
>>        else:
>>            errparg = '&err'
>> 
>>        for memb in members:
>>            if memb.optional:
>>                ret += mcgen('''
>>        if (visit_optional(v, "%(name)s", &%(prefix)shas_%(c_name)s)) {
>>    ''',
>>                             prefix=prefix, c_name=c_name(memb.name),
>>                             name=memb.name, errp=errparg)
>>                push_indent()
>> 
>> errp=errparg unused here.  Not this patch's job to clean up.
>
> Bah. Commit 5cdc8831 missed it, due to repeated refactoring. I'm a bit
> surprised that pep8 didn't complain.  Okay, I'm adding it as a separate
> cleanup.

Thanks!

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop
  2016-01-20 17:15     ` Eric Blake
@ 2016-01-21  7:19       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  7:19 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 09:03 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Inside the generated code between visit_start_struct() and
>>> visit_end_struct(), we were blindly setting the error into
>>> the caller's errp parameter.  But a future patch to split
>>> visit_end_struct() will require that we take action based
>>> on whether an error has occurred, which requires us to track
>>> all actions through a local err.  Rewrite the visits to be
>>> more in line with the other generated calls.
>>>
>
>>> |+    if (!*obj) {
>> 
>> err is clear here.
>> 
>>> |+        goto out_obj;
>>> |+    }
>>> |+    visit_type_GuestAgentCommandInfo_fields(v, obj, &err);
>>> |+out_obj:
>>> |+    error_propagate(errp, err);
>>> |+    err = NULL;
>> 
>> If we come from goto out_obj, these two lines are no-ops.
>
> I could move the label, if desired...
>
>> 
>>> |+    visit_end_struct(v, &err);
>>> |+out:
>>> |     error_propagate(errp, err);
>>> | }
>
>> 
>> gen_visit_union(), the other generator of visit_start_struct(), already
>> does it this way.  It generates additional goto out_obj, so the
>> placement of out_obj makes more sense there.  I guess we want to place
>> it in the same spot here to facilitate unifying the two.
>
> ...but as you observed, the unification in 31 is a bit easier with it
> placed identically.  Maybe a commit message comment is in order.

Or perhaps move it forward now, and move it back when you actually need
it back.

Reviewers appreciate miminal patch churn, but they also appreciate
patches making sense on their own.  Reviewer are looking at your patch
with precious little context in general, and next to zero visibility
into later patches in particular.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks
  2016-01-20 18:10     ` Eric Blake
@ 2016-01-21  8:56       ` Markus Armbruster
  2016-01-21 17:22         ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  8:56 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 10:29 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Our qapi visitor contract supports multiple integer visitors,
>>> but left the type_uint64 visitor as optional (falling back on
>>> type_int64); it also marks the type_size visitor as optional
>>> (falling back on type_uint64 or even type_int64).
>>>
>>> Note that the default of falling back on type_int for unsigned
>>> visitors can cause confusing results for values larger than
>>> INT64_MAX (such as having to pass in a negative 2s complement
>>> value on input, and getting a negative result on output).
>>>
>>> This patch does not fully address the disparity in handling
>>> large values as negatives, but does move towards a cleaner
>>> situation where EVERY visitor provides both type_int64 and
>>> type_uint64 variants as entry points; then each client can
>>> either implement sane differences between the two, or document
>>> in place with a FIXME that there is munging going on.
>> 
>> Before: nobody implements type_uint64(), and the core falls back to
>> type_int64(), casting negative values to large positive ones.  With an
>> implementation of type_int64() that parses large positive values as
>> negative, the two casts cancel out.
>> 
>> After: everybody implements type_uint64() incorrectly, by reusing
>> type_int64() code.  The problem moves from visitor core to visitor
>> implementations, where we can actually fix it if we care.
>> 
>> Correct?
>
> Close. opts-visitor.c already implemented type_uint64, but also has its
> known bugs (and Paolo has been pinged about his claim for fixes in that
> file...)

Ah, yes.  opts_type_uint64() doesn't reuse opts_type_int64(), but
largely duplicates it.  Potentially less wrong :)

> But otherwise, yes, in this patch, we are not fixing insanity so much as
> localizing and better documenting it.

Let me try to clarify the commit message a bit:

    This patch does not address the disparity in handling large values
    as negatives.  It merely moves the fallback from uint64 to int64
    from the visitor core to the visitors, where the issue can actually
    be fixed, by implementing the missing type_uint64() callbacks on top
    of the respective type_int64() callbacks, with a FIXME comment
    explaining why that's wrong.

> I've given some thought about converting the QObject 'int' type to track
> a sign bit, making it effectively a superset covering 3*2^63 values in
> the range [INT64_MIN, UINT64_MAX], and then enhancing the parser to
> track if a negative value was parsed and the formatter to output
> negative if the sign bit is set, and then make the 'int64' and 'uint64'
> parsers stick to stricter 2*64 subsets of that range.  But I haven't
> actually written a patch along those lines yet.

Yes, we'll want to get that right eventually, but it's not urgent.

>>> The dealloc visitor no longer needs a type_size callback,
>>> since that now safely falls back to the type_uint64 callback.
>> 
>> Did it need the callback before this patch?
>
> Not really.  Should I split out the deletion of that callback as a
> separate patch?

Probably cleanest, but merely adjusting the commit message would also
work for me:

    The dealloc visitor doesn't actually need a type_size() callback,
    since the visitor core can safely fall back to the type_uint64()
    callback.  Drop it.

>>> Then, in qapi-visit-core.c, we can now use the guaranteed
>>> type_uint64 callback as the fallback for all smaller unsigned
>>> int visits.
>> 
>> The type_int64() callback works just fine for smaller unsigned ints, but
>> I agree avoiding mixed signedness by using type_uint64() make sense once
>> type_uint64() is mandatory.
>> 
>
>>> +++ b/qapi/qapi-visit-core.c
>>> @@ -102,15 +102,15 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>>>
>>>  void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
>>>  {
>>> -    int64_t value;
>>> +    uint64_t value;
>>>
>>>      if (v->type_uint8) {
>>>          v->type_uint8(v, obj, name, errp);
>>>      } else {
>>>          value = *obj;
>>> -        v->type_int64(v, &value, name, errp);
>>> -        if (value < 0 || value > UINT8_MAX) {
>>> -            /* FIXME questionable reuse of errp if type_int64() changes
>>> +        v->type_uint64(v, &value, name, errp);
>>> +        if (value > UINT8_MAX) {
>>> +            /* FIXME questionable reuse of errp if type_uint64() changes
>>>                 value on error */
>>>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
>>>                         name ? name : "null", "uint8_t");
>> 
>> You could delay adding these FIXMEs until this patch, and reduce churn.
>> Probably not worth the bother now.
>
> Yeah, I'll see how the rest of the series review goes.

Hope to make some more progress today.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks
  2016-01-20 18:17     ` Eric Blake
@ 2016-01-21  9:05       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  9:05 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 10:34 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Commit 4e27e819 introduced optional visitor callbacks for all
>>> sorts of int types, but no visitor has supplied any of the
>>> callbacks for sizes less than 64 bits.  In other words, the
>>> generic implementation based on using type_[u]int64() followed
>>> by bounds-checking works just fine. In the interest of
>>> simplicity, it's easier to make the visitor callback interface
>>> not have to worry about the other sizes.
>>>
>>> Adding some helper functions minimizes the boilerplate required
>>> to correct FIXMEs added earlier with regards to questionable
>>> reuse of errp, particularly now that we can guarantee from a
>>> single file audit that value is unchanged if an error is set.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>>
>>> ---
>>> v9: hoist some of visitor-impl.h changes into 9/35 and 10/35
>>> v8: no change
>>> v7: further factor out helper functions that eliminate the
>>> questionable errp reuse
>>> v6: split off from v5 23/46
>>> original version also appeared in v6-v9 of subset D
>>> ---
>>>  include/qapi/visitor-impl.h |   8 +--
>>>  qapi/qapi-visit-core.c | 158
>>> +++++++++++++++++---------------------------
>>>  2 files changed, 60 insertions(+), 106 deletions(-)
>> 
>> Nice diffstat.
>> 
>>>
>>> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
>>> index 45c1d3e..e6399d1 100644
>>> --- a/include/qapi/visitor-impl.h
>>> +++ b/include/qapi/visitor-impl.h
>>> @@ -1,7 +1,7 @@
>>>  /*
>>>   * Core Definitions for QAPI Visitor implementations
>>>   *
>>> - * Copyright (C) 2012 Red Hat, Inc.
>>> + * Copyright (C) 2012, 2015-2016 Red Hat, Inc.
>> 
>> git-log has authors @redhat.com in 2013 and 2014 as well.
>
> I didn't bother to check whether those edits were complex enough to
> warrant claiming copyright additions; but I'm also amenable to
> shortening to '2012-2016' on a respin regardless of the sizes of those
> edits.  As it is, I was not very careful about adding 2016 in the v9
> spin, so I may be inconsistent on which years are claimed where, in
> comparison to where I felt I was adding new copyrightable content rather
> than just minor fixing of existing content.

IANAL, but I feel claiming 2012-2016 when some of the years in the
middle saw only changes of debatable copyrightability is a pardonable
sin.

> [While it's nice that we DON'T have to assign copyright to a central
> organization to contribute to qemu, sometimes it is much nicer working
> on FSF code where a single copyright holder makes discussions like this
> moot.]
>
>>> +    } else if (value > max) {
>>> +        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
>>> +                   name ? name : "null", type);
>> 
>> We should clean up this name ? name : "null" nonsense some day.
>
> It all stems from whether the visitor is locally visiting { 'name':value
> } vs. [ value ]; in an array visit, there is no direct name.
>
> Maybe we could go a step higher; if we have:
>
> { 'name': [ value ] }
>
> then we could require callers to parse value by passing in "[name]" or
> "name[0]" rather than NULL.  Then audit the code to always pass in a
> sensible name at the top level of a parse.  It would even extend to 2-D
> arrays, via "[[name]]" or "name[0][0]".  But not in this series.

"Parameter 'null' expects int32_t" is not an acceptable error message.
It's better than crashing, but that's about it.

'null' is actively misleading.  The thing we choked on isn't named
'null'.  Even calling it a parameter is questionable.

A fix needs to identify the thing we choked on in a way that let's the
user find it.  '[[name]]' doesn't cut it, I'm afraid.

'expects int32_t' is below par, too.  Sure, a programmer will understand
it fine, but we need to explain things to the user in the user's term,
not in C programming jargon.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int*
  2016-01-20 19:58     ` Eric Blake
@ 2016-01-21  9:08       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  9:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 11:08 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> C compilers are allowed to represent enums as a smaller type
>>> than int, if all enum values fit in the smaller type.  There
>>> are even compiler flags that force the use of this smaller
>>> representation, and using them changes the ABI of a binary.
>> 
>> Suggest "although using them".
>> 
>
> Okay.
>
>
>>> with generated code changing as:
>>>
>>> | void visit_type_GuestDiskBusType(Visitor *v, GuestDiskBusType
>>> | *obj, const char *name, Error **errp)
>>> | {
>>> |-    visit_type_enum(v, (int *)obj, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
>>> |+    int tmp = *obj;
>>> |+    visit_type_enum(v, &tmp, GuestDiskBusType_lookup, "GuestDiskBusType", name, errp);
>>> |+    *obj = tmp;
>>> | }
>> 
>> Long lines.  Do we have an example with a shorter enum name handy?
>
> Shortest is QType; runner-ups RxState and TpmType.

QType comes out okay:

| void visit_type_QType(Visitor *v, QType *obj, const char *name, Error **errp)
| {
|-    visit_type_enum(v, (int *)obj, QType_lookup, "QType", name, errp);
|+    int tmp = *obj;
|+     visit_type_enum(v, &tmp, QType_lookup, "QType", name, errp);
|+    *obj = tmp;
| }

Let's use it.

>>>  void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
>>>  {
>>> -    visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
>>> +    int tmp = *obj;
>>> +    visit_type_enum(v, &tmp, %(c_name)s_lookup, "%(name)s", name, errp);
>>> +    *obj = tmp;
>>>  }
>>>  ''',
>>>                   c_name=c_name(name), name=name)
>> 
>> Same pattern in qapi-visit-core.c, except we name the variable @value
>> there.  Your choice.
>
> 'value' sounds consistent. An easy swap on a respin.

Okay.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  2016-01-20 20:54     ` Eric Blake
@ 2016-01-21  9:18       ` Markus Armbruster
  2016-01-21  9:46         ` Kevin Wolf
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  9:18 UTC (permalink / raw)
  To: Eric Blake
  Cc: Kevin Wolf, Paolo Bonzini, Eduardo Habkost, open list:nvme,
	Michael S. Tsirkin, Jiri Slaby, Jason Wang, qemu-devel,
	Alexander Graf, Keith Busch, Gonglei, open list:sPAPR,
	Gerd Hoffmann, Igor Mammedov, marcandre.lureau, David Gibson,
	John Snow, Andreas Färber, Richard Henderson

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 11:49 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Similar to the previous patch, it's nice to have all functions
>>> in the tree that involve a visitor and a name for conversion to
>>> or from QAPI to consistently stick the 'name' parameter next
>>> to the Visitor parameter.
>>>
>>> Done by manually changing include/qom/object.h and qom/object.c,
>>> then running this Coccinelle script and touching up the fallout
>>> (Coccinelle insisted on adding some trailing whitespace).
>>>
>>>     @ rule1 @
>>>     identifier fn;
>>>     type Object, Visitor, Error;
>>>     identifier obj, v, opaque, name, errp;
>>>     @@
>>>      void fn
>>>     - (Object *obj, Visitor *v, void *opaque, const char *name,
>>>     + (Object *obj, Visitor *v, const char *name, void *opaque,
>>>        Error **errp) { ... }
>> 
>> I think we want to match void functions with exactly these parameter
>> types.  The parameter names don't matter.
>
> The parameter names shouldn't matter; the 'identifier obj' should have
> been enough to make 'obj' a metavariable matching any actual parameter name.
>
>> 
>> However, the actual match is looser!  For instance, it also matches
>> 
>>     void foo(int *pi, unsigned *pu, void *vp, const char *cp, double **dpp)
>>     {
>>     }
>
> Uggh. My intent was to match exactly 'Object *' and 'Visitor *' as the
> first two types, where 'int *' and 'unsigned *' are NOT matches.  But I
> don't know Coccinelle well enough to make that blatantly obvious (is my
> declaration of 'type Object' not correct?).

'type Object' makes 'Object' a metavariable matching any C type.

>> This could mess up unrelated function.  I could double-check it doesn't,
>> but I'd rather have a narrower match instead.  Can't give one offhand,
>> though.  Ideas?
>
> Is 'typedef' better than 'type' for constraining the type of the first
> two arguments?

Matches any C typedef name.  Less wrong, but still wrong :)

>                 Or does Coccinelle do literal matches on anything you
> don't pre-declare, as in:

Yes, but...

>      @ rule1 @
>      identifier fn;
>      identifier obj, v, opaque, name, errp;
>      @@
>       void fn
>      - (Object *obj, Visitor *v, void *opaque, const char *name,
>      + (Object *obj, Visitor *v, const char *name, void *opaque,
>         Error **errp) { ... }

... when I try that, spatch throws a parse error.

> Fortunately, a manual inspection of the results (which I had to do
> anyways due to spacing issues) didn't spot any incorrect swaps.
>
> At this point, I don't know that re-writing Coccinelle will be worth the
> hassle (nothing else needs to be rewritten).

I'd normally take up the challenge to wrestle Coccinelle, but I think
our time is better spent elsewhere right now.  Let's amend the commit
message instead: describe the semantic patch's shortcomings, and your
manual checking:

    This semantic patch is actually far too lose: it matches *any* type,
    not just Object, Visitor, Error.  However, I can't figure out how to
    make Coccinelle match Object, Visitor, Error exactly.  Simply
    deleting the 'type Object, Visitor, Error;' line results in a parse
    error.  I reviewed the resulting patch carefully to verify there are
    no unwanted matches.

>>>     @@
>>>     identifier rule1.fn;
>>>     expression obj, v, opaque, name, errp;
>>>     @@
>>>      fn(obj, v,
>>>     -   opaque, name,
>>>     +   name, opaque,
>>>         errp)
>> 
>> The rule1.fn restricts the match to functions changed by the previous
>> rule.  Good.
>> 
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> 

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API
  2016-01-20 21:01     ` Eric Blake
@ 2016-01-21  9:19       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  9:19 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 11:55 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> As explained in the previous patches, matching argument order of
>>> 'name, &value' to JSON's "name":value makes sense.  However,
>>> while the last two patches were easy with Coccinelle, I ended up
>>> doing this one all by hand.  Now all the visitor callbacks match
>>> the main interface.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>>
>
>>> @@ -30,39 +30,42 @@ struct Visitor
>>>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>>>      void (*end_list)(Visitor *v, Error **errp);
>>>
>>> -    void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
>>> -                      const char *kind, const char *name, Error **errp);
>>> +    void (*type_enum)(Visitor *v, const char *name, int *obj,
>>> +                      const char * const strings[], const char *kind,
>> 
>> Opportunity to change to 'const char *const'.  I prefer that, because it
>> makes the fact that this is a pointer-* and not a binary operator-*
>> visually obvious.
>> 
>> Same elsewhere.
>
> Hmm, I probably have churn later in the series.  Will fix.
>
>>>      /* May be NULL; most useful for input visitors. */
>>> -    void (*optional)(Visitor *v, bool *present, const char *name);
>>> +    void (*optional)(Visitor *v, const char *name, bool *present);
>>>
>
>> 
>> I checked the changes to this file carefully.  Can we rely on the
>> compiler to flag mistakes in the rest of the patch?
>
> C's (intentionally-loose) treatment of 'char *' like 'void *' is a bit
> worrisome, but the fact that we have 'const' on only one of the two
> swapped arguments was indeed enough to make the compiler complain about
> mismatch in parameter types when trying to assign incorrectly-typed
> static functions to the updated struct members.

Okay, I guess that's good enough.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  2016-01-21  9:18       ` Markus Armbruster
@ 2016-01-21  9:46         ` Kevin Wolf
  2016-01-21 10:04           ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Kevin Wolf @ 2016-01-21  9:46 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Paolo Bonzini, Eduardo Habkost, open list:nvme,
	Michael S. Tsirkin, Jason Wang, qemu-devel, Alexander Graf,
	Keith Busch, Gonglei, open list:sPAPR, Gerd Hoffmann,
	Igor Mammedov, marcandre.lureau, Jiri Slaby, David Gibson,
	John Snow, Andreas Färber, Richard Henderson

Am 21.01.2016 um 10:18 hat Markus Armbruster geschrieben:
> Eric Blake <eblake@redhat.com> writes:
> > On 01/20/2016 11:49 AM, Markus Armbruster wrote:
> >> Eric Blake <eblake@redhat.com> writes:
> >> 
> >> However, the actual match is looser!  For instance, it also matches
> >> 
> >>     void foo(int *pi, unsigned *pu, void *vp, const char *cp, double **dpp)
> >>     {
> >>     }
> >
> > Uggh. My intent was to match exactly 'Object *' and 'Visitor *' as the
> > first two types, where 'int *' and 'unsigned *' are NOT matches.  But I
> > don't know Coccinelle well enough to make that blatantly obvious (is my
> > declaration of 'type Object' not correct?).
> 
> 'type Object' makes 'Object' a metavariable matching any C type.

I can't say anything on this one, because I've never used 'type'. You
may or may not be right. However...

> >> This could mess up unrelated function.  I could double-check it doesn't,
> >> but I'd rather have a narrower match instead.  Can't give one offhand,
> >> though.  Ideas?
> >
> > Is 'typedef' better than 'type' for constraining the type of the first
> > two arguments?
> 
> Matches any C typedef name.  Less wrong, but still wrong :)

...I'm pretty sure that this is wrong and 'typedef' only declares that a
specific type exists as a typedef.

> >                 Or does Coccinelle do literal matches on anything you
> > don't pre-declare, as in:
> 
> Yes, but...
> 
> >      @ rule1 @
> >      identifier fn;
> >      identifier obj, v, opaque, name, errp;
> >      @@
> >       void fn
> >      - (Object *obj, Visitor *v, void *opaque, const char *name,
> >      + (Object *obj, Visitor *v, const char *name, void *opaque,
> >         Error **errp) { ... }
> 
> ... when I try that, spatch throws a parse error.

Because you need to declare the typedef. :-)

I had a similar problem and asked Julia about it at KVM Forum, so I'm
pretty sure that it's right.

Kevin

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct
  2016-01-20 21:58     ` Eric Blake
@ 2016-01-21  9:47       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21  9:47 UTC (permalink / raw)
  To: Eric Blake
  Cc: Alexander Graf, qemu-devel, Michael Roth, open list:sPAPR,
	marcandre.lureau, David Gibson

Eric Blake <eblake@redhat.com> writes:

> On 01/20/2016 12:03 PM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> No backend was setting an error when ending the visit of a list
>>> or implicit struct.
>> 
>> That's a lie: qmp_input_end_list() does.  But it shouldn't, as you
>> explain below.  Rephrase the commit message?
>
> I'm not sure why you call it a lie - qmp_input_end_list() will not set
> an error unless it is mistakenly paired with a push(struct), which none
> of our code base does.  Or put another way, although qmp_input_pop()
> [called by qmp_input_end_list()] has a signature that can set an error,
> closer inspection shows that it will only do so when invoked to close
> out a struct, and not when closing out a list.  But that's a blatant
> programmer mismatch, which none of our code base does, so no well-formed
> use of visitors can cause qmp_input_end_list() to set an error.

Okay, it's not a lie if you consider the whole program.  It looks like a
lie locally.

>>>                      Make the callers a bit easier to follow by
>>> making this a part of the contract, and removing the errp
>>> argument - callers can then unconditionally end an object as
>>> part of cleanup without having to think about whether a second
>>> error is dominated by a first, because there is no second error.
>>>
>>> The only addition of &error_abort in this patch, in the function
>>> qmp_input_end_list(), will never trigger unless a programming
>>> bug creates a push(struct)/pop(list) or push(list)/pop(struct)
>>> mismatch.
>
> I'm open to wording suggestions.
>
> Maybe replace all of the above with:
>
> None of the existing .end_implicit_struct() implementations use errp.
> And of the existing .end_list() implementations, only
> qmp_input_end_list() even uses errp, but closer inspection shows that it
> will never be modified (errp is only passed to qmp_input_pop(), which
> will only set an error if the corresponding push was a struct rather
> than a list).  We can turn that internal usage into an &error_abort, to
> protect against programmer mistakes of push(struct)/pop(list) or
> push(list)/pop(struct) mismatch.
>
> With that done, we can then make all public uses of
> visit_end_implicit_struct() and visit_end_list() easier to follow by
> removing the errp argument and making error-free operation part of the
> contract.  Callers can then unconditionally end an object as part of
> cleanup without having to think about whether a second error is
> dominated by a first, because there is no possibility of a second error.

Works for me.

>>> A later patch will then tackle the larger task of splitting
>>> visit_end_struct(), which can indeed set an error (and that
>>> cleanup will also have the side-effect of removing the use of
>>> error_abort added here).
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> 
>> Patch looks good.  I like the simplification.
>
> Would help to split this into two patches, one switching from
> qmp_input_pop(errp) into qmp_input_pop(&error_abort), and the other then
> removing unused errp argument?

If it results in more readable commit messages.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor
  2016-01-21  9:46         ` Kevin Wolf
@ 2016-01-21 10:04           ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21 10:04 UTC (permalink / raw)
  To: Kevin Wolf
  Cc: Eduardo Habkost, open list:nvme, Michael S. Tsirkin, John Snow,
	Jason Wang, qemu-devel, Alexander Graf, Keith Busch,
	marcandre.lureau, Gonglei, open list:sPAPR, Gerd Hoffmann,
	Igor Mammedov, Paolo Bonzini, Richard Henderson, Jiri Slaby,
	Andreas Färber, David Gibson

Kevin Wolf <kwolf@redhat.com> writes:

> Am 21.01.2016 um 10:18 hat Markus Armbruster geschrieben:
>> Eric Blake <eblake@redhat.com> writes:
>> > On 01/20/2016 11:49 AM, Markus Armbruster wrote:
>> >> Eric Blake <eblake@redhat.com> writes:
>> >> 
>> >> However, the actual match is looser!  For instance, it also matches
>> >> 
>> >>     void foo(int *pi, unsigned *pu, void *vp, const char *cp, double **dpp)
>> >>     {
>> >>     }
>> >
>> > Uggh. My intent was to match exactly 'Object *' and 'Visitor *' as the
>> > first two types, where 'int *' and 'unsigned *' are NOT matches.  But I
>> > don't know Coccinelle well enough to make that blatantly obvious (is my
>> > declaration of 'type Object' not correct?).
>> 
>> 'type Object' makes 'Object' a metavariable matching any C type.
>
> I can't say anything on this one, because I've never used 'type'. You
> may or may not be right. However...
>
>> >> This could mess up unrelated function.  I could double-check it doesn't,
>> >> but I'd rather have a narrower match instead.  Can't give one offhand,
>> >> though.  Ideas?
>> >
>> > Is 'typedef' better than 'type' for constraining the type of the first
>> > two arguments?
>> 
>> Matches any C typedef name.  Less wrong, but still wrong :)
>
> ...I'm pretty sure that this is wrong and 'typedef' only declares that a
> specific type exists as a typedef.

Coccinelle's documentation is awfully terse.  Looks like I jumped to
conclusions.

>> >                 Or does Coccinelle do literal matches on anything you
>> > don't pre-declare, as in:
>> 
>> Yes, but...
>> 
>> >      @ rule1 @
>> >      identifier fn;
>> >      identifier obj, v, opaque, name, errp;
>> >      @@
>> >       void fn
>> >      - (Object *obj, Visitor *v, void *opaque, const char *name,
>> >      + (Object *obj, Visitor *v, const char *name, void *opaque,
>> >         Error **errp) { ... }
>> 
>> ... when I try that, spatch throws a parse error.
>
> Because you need to declare the typedef. :-)
>
> I had a similar problem and asked Julia about it at KVM Forum, so I'm
> pretty sure that it's right.

If I replace 'type' by 'typedef', spatch no longer messes up

    void foo(int *pi, unsigned *pu, void *vp, const char *cp, double **dpp)

but still rewrites

    void bar(Object *o, Visitor *v, void *opq, const char *n, Error **ep)

Thanks, Kevin!

Eric, could you try to regenerate your patch with the corrected script?

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
@ 2016-01-21 10:27   ` Markus Armbruster
  2016-01-21 13:19     ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21 10:27 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-stable, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Commit 6c2f9a15 ensured that we would not return NULL when the
> caller used an output visitor but had nothing to visit. But
> in doing so, it added a FIXME about a reference count leak
> that could abort qemu in the (unlikely) case of SIZE_MAX such
> visits (more plausible on 32-bit).  (Although that commit
> suggested we might fix it in time for 2.5, we ran out of time;
> fortunately, it is unlikely enough to bite that it was not
> worth worrying about during the 2.5 release.)
>
> This fixes things by documenting the internal contracts, and
> explaining why the internal function can return NULL and only
> the public facing interface needs to worry about qnull(),
> thus avoiding over-referencing the qnull_ global object.
>
> It does not, however, fix the stupidity of the stack mixing
> up two separate pieces of information; add a FIXME to explain
> that issue, which will be fixed shortly in a future patch.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Cc: qemu-stable@nongnu.org
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: enhance commit message
> v8: rebase to earlier changes
> v7: cc qemu-stable, tweak some asserts, drop stale comment, add more
> comments
> v6: no change
> ---
>  qapi/qmp-output-visitor.c       | 39 ++++++++++++++++++++++++++++++++-------
>  tests/test-qmp-output-visitor.c |  2 ++
>  2 files changed, 34 insertions(+), 7 deletions(-)
>
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index d367148..316f4e4 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -29,6 +29,15 @@ typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
>  struct QmpOutputVisitor
>  {
>      Visitor visitor;
> +    /* FIXME: we are abusing stack to hold two separate pieces of
> +     * information: the current root object in slot 0, and the stack
> +     * of N objects still being built in slots 1 through N (for N+1
> +     * slots in use).  Worse, our behavior is inconsistent:
> +     * qmp_output_add_obj() visiting two top-level scalars in a row
> +     * discards the first in favor of the second, but visiting two
> +     * top-level objects in a row tries to append the second object
> +     * into the first (since the first object was placed in the stack
> +     * in both slot 0 and 1, but only popped from slot 1).  */
>      QStack stack;
>  };
>
> @@ -41,10 +50,12 @@ static QmpOutputVisitor *to_qov(Visitor *v)
>      return container_of(v, QmpOutputVisitor, visitor);
>  }
>
> +/* Push @value onto the stack of current QObjects being built */
>  static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>  {
>      QStackEntry *e = g_malloc0(sizeof(*e));
>
> +    assert(value);
>      e->value = value;
>      if (qobject_type(e->value) == QTYPE_QLIST) {
>          e->is_list_head = true;
> @@ -52,44 +63,51 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> +/* Grab and remove the most recent QObject from the stack */
>  static QObject *qmp_output_pop(QmpOutputVisitor *qov)
>  {
>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
>      QObject *value;
> +
> +    assert(e);
>      QTAILQ_REMOVE(&qov->stack, e, node);
>      value = e->value;
>      g_free(e);
>      return value;

@value cannot be null here, because we never push nulls.  Worth an
assertion, just to help readers?

>  }
>
> +/* Grab the root QObject, if any, in preparation to empty the stack */

Why is this "in preparation to empty the stack"?

>  static QObject *qmp_output_first(QmpOutputVisitor *qov)
>  {
>      QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
>
> -    /*
> -     * FIXME Wrong, because qmp_output_get_qobject() will increment
> -     * the refcnt *again*.  We need to think through how visitors
> -     * handle null.
> -     */
>      if (!e) {
> -        return qnull();
> +        /* No root */
> +        return NULL;
>      }
> -
> +    assert(e->value);
>      return e->value;
>  }

Two callers to check: qmp_output_get_qobject() and
qmp_output_visitor_cleanup().  See below.

>
> +/* Grab the most recent QObject from the stack, which must exist */
>  static QObject *qmp_output_last(QmpOutputVisitor *qov)
>  {
>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> +
> +    assert(e && e->value);
>      return e->value;
>  }
>
> +/* Add @value to the current QObject being built.
> + * If the stack is visiting a dictionary or list, @value is now owned
> + * by that container. Otherwise, @value is now the root.  */
>  static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>                                 QObject *value)
>  {
>      QObject *cur;
>
>      if (QTAILQ_EMPTY(&qov->stack)) {
> +        /* Stack was empty, track this object as root */
>          qmp_output_push_obj(qov, value);
>          return;
>      }
> @@ -98,13 +116,17 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>
>      switch (qobject_type(cur)) {
>      case QTYPE_QDICT:
> +        assert(name);
>          qdict_put_obj(qobject_to_qdict(cur), name, value);
>          break;
>      case QTYPE_QLIST:
>          qlist_append_obj(qobject_to_qlist(cur), value);
>          break;
>      default:
> +        /* The previous root was a scalar, replace it with a new root */
> +        /* FIXME this is abusing the stack; see comment above */
>          qobject_decref(qmp_output_pop(qov));
> +        assert(QTAILQ_EMPTY(&qov->stack));
>          qmp_output_push_obj(qov, value);
>          break;
>      }
> @@ -205,11 +227,14 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
>      qmp_output_add_obj(qov, name, *obj);
>  }
>
> +/* Finish building, and return the root object. Will not be NULL. */
>  QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
>  {
>      QObject *obj = qmp_output_first(qov);
>      if (obj) {
>          qobject_incref(obj);
> +    } else {
> +        obj = qnull();
>      }

qmp_output_first() now returns NULL instead of qnull().  This changes
compensates; taken together, the beavior is the same, except we avoid
the qobject_incref() in the qnull() case.  That's the bug fix.

Aside: I believe obj could not be null before this patch, so the
conditional was redundant.  No need to complicate the commit message
with this, I think.

>      return obj;
>  }

Unchanged:

   void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
   {
       QStackEntry *e, *tmp;

       /* The bottom QStackEntry, if any, owns the root QObject. See the
        * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
       QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);

Calls qmp_output_first() only when the stack isn't empty, and then
qmp_output_first() behaves exactly as before this patch.  Good.

       QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
           QTAILQ_REMOVE(&v->stack, e, node);
           g_free(e);
       }

       qobject_decref(root);
       g_free(v);
   }

> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index 4df94bc..26dc752 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -461,6 +461,8 @@ static void test_visitor_out_empty(TestOutputVisitorData *data,
>
>      arg = qmp_output_get_qobject(data->qov);
>      g_assert(qobject_type(arg) == QTYPE_QNULL);
> +    /* Check that qnull reference counting is sane */
> +    g_assert(arg->refcnt == 2);
>      qobject_decref(arg);
>  }

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit
  2016-01-21 10:27   ` Markus Armbruster
@ 2016-01-21 13:19     ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21 13:19 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-stable, qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
>
>> Commit 6c2f9a15 ensured that we would not return NULL when the
>> caller used an output visitor but had nothing to visit. But
>> in doing so, it added a FIXME about a reference count leak
>> that could abort qemu in the (unlikely) case of SIZE_MAX such
>> visits (more plausible on 32-bit).  (Although that commit
>> suggested we might fix it in time for 2.5, we ran out of time;
>> fortunately, it is unlikely enough to bite that it was not
>> worth worrying about during the 2.5 release.)
>>
>> This fixes things by documenting the internal contracts, and
>> explaining why the internal function can return NULL and only
>> the public facing interface needs to worry about qnull(),
>> thus avoiding over-referencing the qnull_ global object.
>>
>> It does not, however, fix the stupidity of the stack mixing
>> up two separate pieces of information; add a FIXME to explain
>> that issue, which will be fixed shortly in a future patch.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Cc: qemu-stable@nongnu.org
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>
>> ---
>> v9: enhance commit message
>> v8: rebase to earlier changes
>> v7: cc qemu-stable, tweak some asserts, drop stale comment, add more
>> comments
>> v6: no change
>> ---
>>  qapi/qmp-output-visitor.c       | 39 ++++++++++++++++++++++++++++++++-------
>>  tests/test-qmp-output-visitor.c |  2 ++
>>  2 files changed, 34 insertions(+), 7 deletions(-)
>>
>> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
>> index d367148..316f4e4 100644
>> --- a/qapi/qmp-output-visitor.c
>> +++ b/qapi/qmp-output-visitor.c
[...]
>> @@ -41,10 +50,12 @@ static QmpOutputVisitor *to_qov(Visitor *v)
>>      return container_of(v, QmpOutputVisitor, visitor);
>>  }
>>
>> +/* Push @value onto the stack of current QObjects being built */
>>  static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>>  {
>>      QStackEntry *e = g_malloc0(sizeof(*e));
>>
>> +    assert(value);
>>      e->value = value;
>>      if (qobject_type(e->value) == QTYPE_QLIST) {
>>          e->is_list_head = true;
>> @@ -52,44 +63,51 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>>  }
>>
>> +/* Grab and remove the most recent QObject from the stack */

Let's stick to the stack terminology you used for qmp_output_push_obj():

/* Pop a value off the stack of QObjects being built, and return it */

>>  static QObject *qmp_output_pop(QmpOutputVisitor *qov)
>>  {
>>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
>>      QObject *value;
>> +
>> +    assert(e);
>>      QTAILQ_REMOVE(&qov->stack, e, node);
>>      value = e->value;
>>      g_free(e);
>>      return value;
>
> @value cannot be null here, because we never push nulls.  Worth an
> assertion, just to help readers?
>
>>  }
[...]
>> +/* Grab the most recent QObject from the stack, which must exist */

Stack terminology:

/*
 * Peek at the top of the stack of QObject being built.
 * The stack must not be empty.
 */

>>  static QObject *qmp_output_last(QmpOutputVisitor *qov)
>>  {
>>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
>> +
>> +    assert(e && e->value);
>>      return e->value;
>>  }
[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root Eric Blake
@ 2016-01-21 13:58   ` Markus Armbruster
  2016-01-29  3:06     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21 13:58 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The previous commit documented an inconsistency in how we are
> using the stack of qmp-output-visitor.  Normally, pushing a
> single top-level object puts the object on the stack twice:
> once as the root, and once as the current container being
> appended to; but popping that struct only pops once.  However,
> qmp_ouput_add() was trying to either set up the added object
> as the new root (works if you parse two top-level scalars in a
> row: the second replaces the first as the root) or as a member
> of the current container (works as long as you have an open
> container on the stack; but if you have popped the first
> top-level container, it then resolves to the root and still
> tries to add into that existing container).
>
> Fix the stupidity by not tracking two separate things in the
> stack.  Drop the now-useless qmp_output_first() while at it.
>
> Saved for a later patch: we still are rather sloppy in that
> qmp_output_get_object() can be called in the middle of a parse,
> rather than requiring that a visit is complete.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to earlier changes
> v7: retitle; rebase to earlier changes, drop qmp_output_first()
> v6: no change
> ---
>  qapi/qmp-output-visitor.c | 79 ++++++++++++++++-------------------------------
>  1 file changed, 26 insertions(+), 53 deletions(-)
>
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 316f4e4..df22999 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -29,16 +29,8 @@ typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
>  struct QmpOutputVisitor
>  {
>      Visitor visitor;
> -    /* FIXME: we are abusing stack to hold two separate pieces of
> -     * information: the current root object in slot 0, and the stack
> -     * of N objects still being built in slots 1 through N (for N+1
> -     * slots in use).  Worse, our behavior is inconsistent:
> -     * qmp_output_add_obj() visiting two top-level scalars in a row
> -     * discards the first in favor of the second, but visiting two
> -     * top-level objects in a row tries to append the second object
> -     * into the first (since the first object was placed in the stack
> -     * in both slot 0 and 1, but only popped from slot 1).  */
> -    QStack stack;
> +    QStack stack; /* Stack of containers that haven't yet been finished */
> +    QObject *root; /* Root of the output visit */
>  };
>
>  #define qmp_output_add(qov, name, value) \
> @@ -55,6 +47,7 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>  {
>      QStackEntry *e = g_malloc0(sizeof(*e));
>
> +    assert(qov->root);

This is safe because every call is preceded by qmp_output_add(), which
creates the root if we don't have one, yet.

>      assert(value);
>      e->value = value;
>      if (qobject_type(e->value) == QTYPE_QLIST) {
> @@ -76,26 +69,12 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
>      return value;
>  }
>
> -/* Grab the root QObject, if any, in preparation to empty the stack */
> -static QObject *qmp_output_first(QmpOutputVisitor *qov)
> -{
> -    QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
> -
> -    if (!e) {
> -        /* No root */
> -        return NULL;
> -    }
> -    assert(e->value);
> -    return e->value;
> -}
> -
> -/* Grab the most recent QObject from the stack, which must exist */
> +/* Grab the most recent QObject from the stack, if any */
>  static QObject *qmp_output_last(QmpOutputVisitor *qov)
>  {
>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
>
> -    assert(e && e->value);
> -    return e->value;
> +    return e ? e->value : NULL;
>  }

Okay, because...

>
>  /* Add @value to the current QObject being built.
> @@ -106,29 +85,25 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>  {
>      QObject *cur;
>
> -    if (QTAILQ_EMPTY(&qov->stack)) {
> -        /* Stack was empty, track this object as root */
> -        qmp_output_push_obj(qov, value);
> -        return;
> -    }
> -
>      cur = qmp_output_last(qov);

... this is the only caller, and...

>
> -    switch (qobject_type(cur)) {
> -    case QTYPE_QDICT:
> -        assert(name);
> -        qdict_put_obj(qobject_to_qdict(cur), name, value);
> -        break;
> -    case QTYPE_QLIST:
> -        qlist_append_obj(qobject_to_qlist(cur), value);
> -        break;
> -    default:
> -        /* The previous root was a scalar, replace it with a new root */
> -        /* FIXME this is abusing the stack; see comment above */
> -        qobject_decref(qmp_output_pop(qov));
> -        assert(QTAILQ_EMPTY(&qov->stack));
> -        qmp_output_push_obj(qov, value);
> -        break;
> +    if (!cur) {

... you add the null check.

> +        /* FIXME we should require the user to reset the visitor, rather
> +         * than throwing away the previous root */
> +        qobject_decref(qov->root);
> +        qov->root = value;
> +    } else {
> +        switch (qobject_type(cur)) {
> +        case QTYPE_QDICT:
> +            assert(name);
> +            qdict_put_obj(qobject_to_qdict(cur), name, value);
> +            break;
> +        case QTYPE_QLIST:
> +            qlist_append_obj(qobject_to_qlist(cur), value);
> +            break;
> +        default:
> +            g_assert_not_reached();

We usually just abort().

> +        }
>      }
>  }

Let's see how this works.

We have a root and a stack of containers.

qmp_output_visitor_new() creates no root and an empty stack.

qmp_output_visitor_cleanup() empties the stack and throws away the root.

Visitors of scalars call qmp_output_add_obj().  Visitors of containers
(object a.k.a. struct or array a.k.a. list) do the same, and
additionally call qmp_output_push_obj(), in their start method.  They
call qmp_output_pop() in their end method.  What does this do?

If the stack is empty (!cur), visiting a value replaces the root, if
any.  Begs the question how can we have a root, but let's ignore that
for now.

If the stack isn't empty, we add to the container on the top of the
stack.

If we're starting to visit a container, we push it onto the stack.  It
remains there until we end the container's visit.

Okay, this works.

After an outermost visit completes, the stack is empty, but the root
lingers.  It gets thrown away only when you reuse the visitor for
another visit (do we do that?), or you destroy the visitor.  Letting the
root linger that way isn't nice, and that's what your FIXME points out.

If you abort a visit half-way through, the stack may be non-empty.  You
can't just start another visit then.  You have to destroy the visitor
and create a new one.

If we required a "reset" between visits, as your FIXME suggests, the
difference between completed and aborted visits can be removed.

>
> @@ -230,7 +205,9 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
>  /* Finish building, and return the root object. Will not be NULL. */
>  QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
>  {
> -    QObject *obj = qmp_output_first(qov);
> +    /* FIXME: we should require that a visit occurred, and that it is
> +     * complete (no starts without a matching end) */

I agree the visit must complete before you can retrieve the value.

I think there are two sane ways to recover from errors:

1. Require the client to empty the stack by calling the necessary end
   methods.

2. Allow the client to reset or destroy the visitor without calling end
   methods.

*This* visitor would be fine with either.  I guess the others would be
fine, too.  So it's a question of interface design.

I'm currently leaning towards 2, because "you must do A, B and C before
you can destroy this object" would be weird.  What do you think?

> +    QObject *obj = qov->root;
>      if (obj) {
>          qobject_incref(obj);
>      } else {
> @@ -248,16 +225,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
>  {
>      QStackEntry *e, *tmp;
>
> -    /* The bottom QStackEntry, if any, owns the root QObject. See the
> -     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
> -    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
> -

If we require end methods to be called, the stack must be empty here,
rendering the following loop useless.

>      QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
>          QTAILQ_REMOVE(&v->stack, e, node);
>          g_free(e);
>      }
>
> -    qobject_decref(root);
> +    qobject_decref(v->root);
>      g_free(v);
>  }

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling
  2016-01-21  6:21       ` Markus Armbruster
@ 2016-01-21 17:12         ` Eric Blake
  2016-01-21 17:29         ` Daniel P. Berrange
  1 sibling, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-21 17:12 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Luiz Capitulino

[-- Attachment #1: Type: text/plain, Size: 2772 bytes --]

On 01/20/2016 11:21 PM, Markus Armbruster wrote:
>> One alternative is to always output a guaranteed unambiguous decimal
>> string (although not necessarily the shortest), by using %.17f, using
>> <float.h> DBL_DECIMAL_DIG.  (Note that DBL_DIG of 15 is NOT sufficient -
>> it is the lower limit that says that a decimal->float->decimal will not
>> change the decimal; but we want the converse where a
>> float->decimal->float will not change the float.  There are stretches of
>> numbers where the pigeonhole principle applies; you can think of it this
>> way: there is no way to map all possible 2^10 (1024) binary values
>> inside 2^3 (1000) decimal digits without at least 24 of them needing one
>> more decimal digit.  But by the same arguments, DBL_DECIMAL_DIG is an
>> upper limit and usually more than you need.)
>>
>> So, the question is whether we want to always output 17 digits, or
>> whether we want to do the poor-man's truncation scheme (easy to
>> understand, but not optimal use of the processor), or go all the way to
>> the algorithm of that paper (faster but lots harder to understand).  For
>> reference, here's the poor-man's algorithm in pseudocode:
> 
> I don't think we want to implement floating-point formatting ourselves.

Well, we already have _some_ level of floating-point support built in
for TCG emulation of floating point on various architectures.  I don't
know how much would be easily reusable for this case, though.

> 
>> if 0, inf, nan:
>>     special case
>> else:
>>     Obtain the DBL_DECIMAL_DIG string via sprintf %.17f
>>     i = 17;
>>     do {
>>         truncate the original string to i-1 decimal characters
>>         parse that with strtod()
>>         if the bit pattern differs:
>>             break;
>>     } while (--i);
>>     assert(i)
>>     use i digits of the string
> 
> That's a lot of strtod()...  May not be noticable if we write the result
> to a slowish sink.  Binary search could save a few.
> 
> Naive idea: chop off trailing '0'?

That, and rounding trailing '9'.  Any other digits in positions 1-16 are
significant (other digits in position 17 might not matter, though), so I
guess a full 17 iterations is probably not strictly necessary.  Our
current code DOES chop trailing '0', but starting from the flawed
premise that 6 digits was enough precision.

> 
>> As a separate patch, of course, but I have a pending patch that provides
>> a single place where we could drop in such an improvement:
>> https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg03932.html
> 
> Definitely separate.

Okay, all thoughts for a later day :)

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks
  2016-01-21  8:56       ` Markus Armbruster
@ 2016-01-21 17:22         ` Eric Blake
  0 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-21 17:22 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 2302 bytes --]

On 01/21/2016 01:56 AM, Markus Armbruster wrote:

>>> Before: nobody implements type_uint64(), and the core falls back to
>>> type_int64(), casting negative values to large positive ones.  With an
>>> implementation of type_int64() that parses large positive values as
>>> negative, the two casts cancel out.
>>>
>>> After: everybody implements type_uint64() incorrectly, by reusing
>>> type_int64() code.  The problem moves from visitor core to visitor
>>> implementations, where we can actually fix it if we care.
>>>
>>> Correct?
>>
>> Close. opts-visitor.c already implemented type_uint64, but also has its
>> known bugs (and Paolo has been pinged about his claim for fixes in that
>> file...)
> 
> Ah, yes.  opts_type_uint64() doesn't reuse opts_type_int64(), but
> largely duplicates it.  Potentially less wrong :)
> 
>> But otherwise, yes, in this patch, we are not fixing insanity so much as
>> localizing and better documenting it.
> 
> Let me try to clarify the commit message a bit:
> 
>     This patch does not address the disparity in handling large values
>     as negatives.  It merely moves the fallback from uint64 to int64
>     from the visitor core to the visitors, where the issue can actually
>     be fixed, by implementing the missing type_uint64() callbacks on top
>     of the respective type_int64() callbacks, with a FIXME comment
>     explaining why that's wrong.

Looks good.  I think we're starting to rack up enough tweaks to make it
worth me posting a v10 respin to fold them all in.

>>>> The dealloc visitor no longer needs a type_size callback,
>>>> since that now safely falls back to the type_uint64 callback.
>>>
>>> Did it need the callback before this patch?
>>
>> Not really.  Should I split out the deletion of that callback as a
>> separate patch?
> 
> Probably cleanest, but merely adjusting the commit message would also
> work for me:
> 
>     The dealloc visitor doesn't actually need a type_size() callback,
>     since the visitor core can safely fall back to the type_uint64()
>     callback.  Drop it.

Okay, I won't bother to split this one; the commit message tweak seems
sufficient.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling
  2016-01-21  6:21       ` Markus Armbruster
  2016-01-21 17:12         ` Eric Blake
@ 2016-01-21 17:29         ` Daniel P. Berrange
  1 sibling, 0 replies; 128+ messages in thread
From: Daniel P. Berrange @ 2016-01-21 17:29 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Luiz Capitulino

On Thu, Jan 21, 2016 at 07:21:49AM +0100, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
> > On 01/20/2016 02:02 AM, Markus Armbruster wrote:
> >
> >>> @@ -519,6 +519,8 @@ static QObject *parse_literal(JSONParserContext *ctxt)
> >>>      }
> >>>      case JSON_FLOAT:
> >>>          /* FIXME dependent on locale */
> >>> +        /* FIXME our lexer matches RFC7159 in forbidding Inf or NaN,
> >> 
> >> For what it's worth, the RFC spells this "RFC 7159".
> >
> > Looks like we use space more often than not, but that we're
> > inconsistent.  For example:
> >
> > slirp/tcp.h: * Per RFC 793, September, 1981.
> > slirp/tcp.h: * Per RFC793, September, 1981.
> >
> > Will fix if I need to respin, otherwise I assume you can do it.
> 
> Okay.
> 
> >>> +        /* FIXME: snprintf() is locale dependent; but JSON requires
> >>> +         * numbers to be formatted as if in the C locale. */
> >> 
> >> The new FIXME matches the existing one in parse_literal(), visible
> >> above.
> >> 
> >> However, dependence on C locale is a pervasive issue in QEMU.  These two
> >> comments could give readers the idea that it's an issue just here.
> >> Suggest to add something like "Dependence on C locale is a pervasive
> >> issue in QEMU."
> >
> > Good idea.
> >
> >> 
> >>> +        /* FIXME: This risks printing Inf or NaN, which are not valid
> >>> +         * JSON values. */
> >>> +        /* FIXME: the default precision of %f may be insufficient to
> >>> +         * tell this number apart from others. */
> >> 
> >> Yup.
> >> 
> >> The easy way to avoid loss of precision is %a, but of course that's way
> >> too much sophistication for JSON.
> >> 
> >> Avoiding loss of precision with a decimal format is non-trivial; see
> >> Steele, Jr., Guy L., and White, Jon L. How to print floating-point
> >> numbers accurately, SIGPLAN ’90, and later improvements collected here:
> >> http://stackoverflow.com/questions/7153979/algorithm-to-convert-an-ieee-754-double-to-a-string
> >
> > Ah, memories.  I read and implemented that paper when working on the
> > jikes compiler for the Java language back in the late nineties, as it is
> > the Java language specification which had the very neat property of
> > requiring the shortest decimal string that will unambiguously round back
> > to the same floating point pattern.
> >
> > One alternative is to always output a guaranteed unambiguous decimal
> > string (although not necessarily the shortest), by using %.17f, using
> > <float.h> DBL_DECIMAL_DIG.  (Note that DBL_DIG of 15 is NOT sufficient -
> > it is the lower limit that says that a decimal->float->decimal will not
> > change the decimal; but we want the converse where a
> > float->decimal->float will not change the float.  There are stretches of
> > numbers where the pigeonhole principle applies; you can think of it this
> > way: there is no way to map all possible 2^10 (1024) binary values
> > inside 2^3 (1000) decimal digits without at least 24 of them needing one
> > more decimal digit.  But by the same arguments, DBL_DECIMAL_DIG is an
> > upper limit and usually more than you need.)
> >
> > So, the question is whether we want to always output 17 digits, or
> > whether we want to do the poor-man's truncation scheme (easy to
> > understand, but not optimal use of the processor), or go all the way to
> > the algorithm of that paper (faster but lots harder to understand).  For
> > reference, here's the poor-man's algorithm in pseudocode:
> 
> I don't think we want to implement floating-point formatting ourselves.

FWIW in libvirt we use per-thread locale switching, so that we can flip
into the C locale temporarily when formatting doubles. If those are not
available we have a bit of a nasty hack to post-process the locale
dependant string back into what we (hope) is C-locale formatting.
We don't do anything aobut the Inf/NaN problem though.

http://libvirt.org/git/?p=libvirt.git;a=blob;f=src/util/virutil.c;h=bb9604a0c1ffb9c99e454e84878a8c376f773046;hb=HEAD#l454

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions Eric Blake
@ 2016-01-21 20:08   ` Markus Armbruster
  2016-01-22  0:30     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-21 20:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

All right, this one's a bear.  Not because the patch is bad, but because
what it tries to do is bloody difficult.

Eric Blake <eblake@redhat.com> writes:

> The visitor interface for mapping between QObject/QemuOpts/string
> and qapi has formerly been documented only by reading source code,

Polite way to say "is scandalously undocumented".

> making it difficult to propose changes to either scripts/qapi*.py
> or to clients without knowing whether those changes would be safe.
> This adds documentation, including mentioning when parameters can
> be NULL, and where there are still some interface warts that would
> be nice to remove.  In particular, I have plans to remove
> visit_start_union() in a future patch.

Suggest

    The visitor interface for mapping between QObject/QemuOpts/string
    and QAPI is pretty much undocumented, making changes to visitor
    core, individual visitors, and users of visitors difficult.

    Correct this by retrofitting proper contracts.  Document some
    interface warts that would be nice to remove.  In particular, I have
    plans to remove visit_start_union() in a future patch.

> Add some asserts to strengthen the claims of the documentation; some
> of these were only made possible by recent cleanup commits.  These
> were made easier with the addition of a new visit_is_output()
> helper (since all 2 output visitors of our 6 overall visitors use
> the same .type_enum() callback).

I find past tense confusing here, it makes me think visit_is_output()
was added in a previous patch.  Perhaps:

    Add assertions to (partially) enforce the contract.  Some of these
    were only made possible by recent cleanup commits.

    Add a new visit_is_output() helper to make the assertions more
    readable.  Its implementation is a bit hacky: it relies on the fact
    that all output visitors use the same .type_enum() callback.

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to 'name' motion
> v7: retitle; more wording changes, add asserts to enforce the
> wording, place later in series to rebase on fixes that would
> otherwise trip the new assertions
> v6: mention that input visitors blindly assign over *obj; wording
> improvements
> ---
>  include/qapi/visitor-impl.h |  31 ++++++-
>  include/qapi/visitor.h      | 196 ++++++++++++++++++++++++++++++++++++++++++++
>  qapi/qapi-visit-core.c      |  39 ++++++++-
>  3 files changed, 262 insertions(+), 4 deletions(-)
>

My review probably makes more sense if you skip ahead to visitor.h, then
come back here.

> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 7f512cf..aab46bc 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -15,23 +15,37 @@
>  #include "qapi/error.h"
>  #include "qapi/visitor.h"
>
> +/* This file describes the callback interface for implementing a
> + * QObject visitor.  For the client interface, see visitor.h.  When

"QAPI visitor", for consistency with visitor.h's and qapi-visit-core.c's
file comment.

> + * implementing the callbacks, it is easiest to declare a struct with
> + * 'Visitor visitor;' as the first member.  Semantics for the
> + * callbacks are generally similar to the counterpart public
> + * interface.  */

Thanks for adding this overview comment.

Suggest to say "A callback's contract matches the corresponding public
functions' contract unless stated otherwise."  Then state otherwise
where needed.

> +
>  struct Visitor
>  {
> -    /* Must be set */
> +    /* Must be provided to visit structs (the string visitors do not
> +     * currently visit structs). */

Uh, the string visitors don't decide what gets visited, their users do.
The string visitors don't support visiting structs.  The restriction
needs to be documented, but this isn't the place to do it, is it?
Suggest to drop the parenthesis.

>      void (*start_struct)(Visitor *v, const char *name, void **obj,
>                           size_t size, Error **errp);
> +    /* Must be provided if start_struct is present. */
>      void (*end_struct)(Visitor *v, Error **errp);
>
> +    /* May be NULL; most useful for input visitors. */

"Optional" would be a bit terser than "May be NULL".

Why is it "most useful for input visitors"?  For what it's worth, the
dealloc visitor finds it useful, too...

>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
>                                    Error **errp);
>      /* May be NULL */
>      void (*end_implicit_struct)(Visitor *v);
>
> +    /* Must be set */
>      void (*start_list)(Visitor *v, const char *name, Error **errp);
> +    /* Must be set */
>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>      /* Must be set */
>      void (*end_list)(Visitor *v);

A visitor could omit these two with similar consequences to omitting
start_struct() and end_struct(): attempts to visit lists crash then.  In
fact, the string visitors omitted them until commit 659268f and 69e2556,
respectively.

>
> +    /* Must be set, although the helpers input_type_enum() and
> +     * output_type_enum() should be used if appropriate.  */

Suggest

    Must be set.  See also helpers input_type_enum(),
    output_type_enum().

>      void (*type_enum)(Visitor *v, const char *name, int *obj,
>                        const char *const strings[], Error **errp);
>      /* May be NULL; only needed for input visitors. */
       void (*get_next_type)(Visitor *v, const char *name, QType *type,
                             bool promote_int, Error **errp);

I figure it must be set for input visitors, because without it, the
generated visit function will assume QTYPE_NONE, and fail.

Suggest "Must be set for input visitors."

> @@ -47,23 +61,38 @@ struct Visitor
>      /* Optional; fallback is type_uint64().  */
>      void (*type_size)(Visitor *v, const char *name, uint64_t *obj,
>                        Error **errp);
> +
>      /* Must be set. */
>      void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp);
> +    /* Must be set */
>      void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp);
> +
> +    /* Must be provided to visit numbers (the opts visitor does not
> +     * currently visit non-integers). */

The restriction needs to be documented, but this isn't the place to do
it.  Suggest to drop the parenthesis.

>      void (*type_number)(Visitor *v, const char *name, double *obj,
>                          Error **errp);
> +    /* Must be provided to visit arbitrary QTypes (the opts and string
> +     * visitors do not currently visit arbitrary types).  */

Likewise.

Looks like we say "must be set to visit this" when at least one visitor
doesn't provide, and "must be set" when all our current visitors
provide.  Hmm.

>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>                       Error **errp);
>
>      /* May be NULL; most useful for input visitors. */
>      void (*optional)(Visitor *v, const char *name, bool *present);
>
> +    /* FIXME - needs to be removed */
>      bool (*start_union)(Visitor *v, bool data_present, Error **errp);
> +    /* FIXME - needs to be removed */
>      void (*end_union)(Visitor *v, bool data_present, Error **errp);
>  };
>
> +/**

The /** is a marker for GTK-Doc.  We don't actually follow GTK-Doc
conventions, however (they suck).  Sure we want the extra * anyway?

> + * A generic visitor.type_enum suitable for input visitors.
> + */
>  void input_type_enum(Visitor *v, const char *name, int *obj,
>                       const char *const strings[], Error **errp);
> +/**
> + * A generic visitor.type_enum suitable for output visitors.
> + */
>  void output_type_enum(Visitor *v, const char *name, int *obj,
>                        const char *const strings[], Error **errp);
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 10390d2..5349a64 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -18,6 +18,20 @@
>  #include "qapi/error.h"
>  #include <stdlib.h>
>
> +/* This file describes the client view for visiting a map between
> + * generated QAPI C structs and another representation (command line
> + * options, strings, or QObjects).

"visiting a map"?

>                                      An input visitor converts from
> + * some other form into QAPI representation; an output visitor
> + * converts from QAPI back into another form.

Let me try to work out what this visitor thingy is about.

The QAPI schema defines a set of QAPI types.  QAPI types can have
members of QAPI type.  A value of such a type is actually a root of a
graph of QAPI values.

QAPI generates visitor functions to walk these graphs.  They currently
work only for trees, because that's all we need.

Walking a tree (or graph for that matter) is useful only to actually do
something with the nodes.  The generated visitor functions do the
walking and the visitor classes do the doing.  This is the visitor
pattern at work: we factor walking the data structure out of the various
tasks that need to walk it to do something.

Three kinds of visitor classes exist: two output visitors (QMP and
string), three input visitors (QMP, string and QemuOpts), and the
dealloc visitor.

With an output visitor, the visitor functions walk a tree, and the
output visitor builds up some output.  QmpOutputVisitor builds a QObject
matching QMP wire format,, and StringOutputVisitor builds a C string.
Both convert a QAPI tree to another representation.

Similarly with the QapiDeallocVisitor, except nothing gets built.
Instead, the tree gets destroyed node by node.

With an input visitor, the visitor functions walk a tree as the input
visitor constructs it.  QmpInputVisitor constructs it from a QObject
matching QMP wire format, StringInputVisitor from a C string, and
OptsVisitor from a QemuOpts.  All three convert from another
representation to a QAPI tree.

The Qmp visitors and the dealloc visitor a general: they can walk /
build any QAPI tree.

The string visitors and the QemuOpts visitor have restrictions: they can
walk / build only a subset.

>                                                 In the descriptions
> + * below, an object or dictionary refers to a JSON '{}', and a list
> + * refers to a JSON '[]'.

We usually call these "JSON object" and "JSON array", following RFC
7159.

>                             These functions seldom need to be called
> + * directly, but are instead used by code generated by
> + * scripts/qapi-visit.py.  For the visitor callback contracts, see
> + * visitor-impl.h.
> + */
> +
> +/* This struct is layout-compatible with all other *List structs
> + * created by the qapi generator. */

QAPI

More instances elsewhere.

>  typedef struct GenericList
>  {
>      union {
> @@ -27,15 +41,101 @@ typedef struct GenericList
>      struct GenericList *next;
>  } GenericList;
>
> +/**
> + * Prepare to visit an object tied to key @name.

Not just any object, but a *struct*.  Suggest:

 * Start visiting struct value @obj.

> + * @name will be NULL if this is visited as part of a list.

I'm afraid reality is a bit messier.

If the visited object is a member of struct or union, @name is its
member name.

If it's a member of a list, @name is null.

If it's a command argument, @name is its parameter name.

If it's a command's result, @name is a meaningless string (qmp-marshal.c
currently uses "unused").

Else, @name can be a meaningless string or null.

Same for other visit functions.

>                                                               The
> + * caller then makes a series of visit calls for each key expected in
> + * the object, where those visits set their respective obj parameter
> + * to the address of a member of the qapi struct, and follows
> + * everything by a call to visit_end_struct() to clean up resources.

I'd explain intended use after the parameters.

> + *
> + * @obj can be NULL (in which case @size should also be 0) to indicate

"must be 0", because you assert that below.

Actually, I find this @size contract weird.  Passing the type's actual
size would make at least as much sense as passing 0.  We generally pass
0 when the size isn't used, but that's just because it's the easiest
value to pass.

> + * that there is no qapi C struct, and that the upcoming visit calls
> + * are parsing input to or creating output from some other
> + * representation.

This is very vague.

Can you point me to code passing null @obj?

I suspect only some visitors accept null @obj.  Which ones?

> + *
> + * If @obj is not NULL, then input visitors malloc a qapi struct of
> + * @size bytes into *@obj on success, and output visitors expect *@obj
> + * to be a fully-populated qapi struct.

Only input visitors and the dealloc visitor use @obj.  The dealloc
visitor doesn't need it in start_struct(), but has to save it for
end_struct(), because that one doesn't get it passed.  Aside: feels
stupid.

Only input visitors use @size, and they use it only when @obj isn't
null.

We could make visitors check the member pointers passed to the member
visits actually point into (@obj, @size).  Then *all* visitors would use
@obj and @size.  I'm not asking for that to be done, I'm just providing
an argument for a tighter contract.  The simplest one would be "Some
visitors permit @obj to be null.  @size must be the struct value's
size."  Except that doesn't match existing usage.  Unless we change it
to match, we need to hedge "except @size is unused and may be anything
when @obj is null", or "except @size is unused and must be zero when
@obj is null".

This method is an awful mess.  Probably because it's serving quite
different purposes in the different visitors.

> + *
> + * Set *@errp on failure; for example, if the input stream does not
> + * have a member @name or if the member is not an object.

What do you mean by "if the member is not an object"?

Any other ways to fail?

This is the only function comment that says anything about @errp.
Should we document the various failure modes everywhere?

> + *
> + * FIXME: For input visitors, *@obj can be assigned here even if later
> + * visits will fail; this can lead to memory leaks if clients aren't
> + * careful.

Why is this a FIXME?  How could it be fixed?  More of the same below.

My attempt at explaining intended use:

    After visit_start_struct() succeeds, the caller may visit its
    members one after the other, passing the member's name and address
    within the struct.  Finally, visit_end_struct() needs to be called
    to clean up.

I guess what the reader really needs here to understand intended use is
example code.  qapi-code-gen.txt has some.  Add a pointer?

> + */
>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp);

Please separate each commented declaration with a blank line.  Not
flagging further instances.

> +/**
> + * Complete a struct visit started earlier.
> + * Must be called after any successful use of visit_start_struct(),
> + * even if intermediate processing was skipped due to errors, to allow
> + * the backend to release any resources.

Actually, destroying the visitor is safe even without calling the
remaining visit_end_struct().  If we introduce a visitor reset as
discussed elsewhere, that could probably be made safe, too.

Same for the other visit_end_FOO().

> + */
>  void visit_end_struct(Visitor *v, Error **errp);
> +
> +/**
> + * Prepare to visit an implicit struct.
> + * Similar to visit_start_struct(), except that an implicit struct
> + * represents a subset of keys that are present at the same nesting level
> + * of a common object but as a separate qapi C struct, rather than a new
> + * object at a deeper nesting level.

I'm afraid this is impenetrable.

I tried to refresh my memory on visit_start_implicit_struct()'s purpose
by reading code, but that's pretty impenetrable, too.

> + *
> + * @obj must not be NULL, since this function is only called when
> + * visiting with qapi structs.

Really?  Why does qmp_input_start_implicit_struct() check for null then?

> + *
> + * FIXME: For input visitors, *@obj can be assigned here even if later
> + * visits will fail; this can lead to memory leaks if clients aren't
> + * careful.
> + */
>  void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
>                                   Error **errp);
> +/**
> + * Complete an implicit struct visit started earlier.
> + * Must be called after any successful use of visit_start_implicit_struct(),
> + * even if intermediate processing was skipped due to errors, to allow
> + * the backend to release any resources.  Unlike visit_end_struct(), this
> + * does not need to check for errors (detection of unused keys is only
> + * possible for the overall struct, not a subset).
> + */
>  void visit_end_implicit_struct(Visitor *v);
>
> +/**
> + * Prepare to visit a list tied to an object key @name.
> + * @name will be NULL if this is visited as part of another list.
> + * After calling this, the elements must be collected until
> + * visit_next_list() returns NULL, then visit_end_list() must be
> + * used to complete the visit.

I'm afraid this doesn't really tell me how to visit a list.  Perhaps:

    After visit_start_list() succeeds, the caller may visit its members
    one after the other, passing the value of visit_next_list() as @obj,
    until visit_next_list() returns NULL.  Finally, visit_end_list()
    needs to be called to clean up.

May want to add a pointer to example code in qapi-code-gen.txt.

> + */
>  void visit_start_list(Visitor *v, const char *name, Error **errp);
> +/**
> + * Iterate over a GenericList during a list visit.
> + * @list must not be NULL; on the first call, @list contains the
> + * address of the list head, and on subsequent calls *@list must be
> + * the previously returned value.  Must be called in a loop until a
> + * NULL return or error occurs; for each non-NULL return, the caller
> + * must then call the appropriate visit_type_*() for the element type
> + * of the list, with that function's name parameter set to NULL.

Isn't must too strong?  Anything horrible's going to happen when you
stop visiting list members early?  I believe not visiting some list
members behaves the same not visiting some struct members: you won't get
the skipped visits' side effects (obviously).  While that may be bad,
it's not *necessarily* bad.  The visitor machinery should cope just fine
regardless.

> + *
> + * Note that for some visitors (qapi-dealloc and qmp-output), when a
> + * qapi GenericList linked list is not being used (comparable to when
> + * a NULL obj is used for visit_start_struct()), it is acceptable to
> + * bypass the use of visit_next_list() and just directly call the
> + * appropriate visit_type_*() for each element in between the
> + * visit_start_list() and visit_end_list() calls.

I'm confused.  Can you point me to code bypassing visit_next_list()?

> + *
> + * FIXME: For input visitors, *@list can be assigned here even if
> + * later visits will fail; this can lead to memory leaks if clients
> + * aren't careful.
> + */
>  GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
> +/**
> + * Complete the list started earlier.
> + * Must be called after any successful use of visit_start_list(),
> + * even if intermediate processing was skipped due to errors, to allow
> + * the backend to release any resources.
> + */
>  void visit_end_list(Visitor *v);
>
>  /**
    * Check if an optional member @name of an object needs visiting.
    * For input visitors, set *@present according to whether the
    * corresponding visit_type_*() needs calling; for other visitors,
    * leave *@present unchanged.  Return *@present for convenience.
   bool visit_optional(Visitor *v, const char *name, bool *present);


Suggest to clarify:

    Does optional struct member @name need visiting?
    @present points to the optional member's has_ flag.
    Input visitors set *@present according to their input.  Other
    visitors leave it unchanged.
    In either case, return *@present.

> @@ -54,32 +154,128 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
   /**
    * Determine the qtype of the item @name in the current object visit.
    * For input visitors, set *@type to the correct qtype of a qapi
    * alternate type; for other visitors, leave *@type unchanged.
    * If @promote_int, treat integers as QTYPE_FLOAT.
>   */
>  void visit_get_next_type(Visitor *v, const char *name, QType *type,
>                           bool promote_int, Error **errp);

Suggest to clarify:

    Determine a visited alternate's QType.
    [Common description of @name goes here]
    @type points to the alternate's type member.
    Input visitors set *@type according to their input.  If
    @promote_int, integer input results in QTYPE_FLOAT.
    Other visitors leave it unchanged.

> +
> +/**
> + * Visit an enum value tied to @name in the current object visit.
> + * @name will be NULL if this is visited as part of a list.
> + * For input visitors, parse a string and set *@obj to the numeric
> + * value of the enum type using @strings as the mapping, leaving @obj
> + * unchanged on error; for output visitors, reverse the mapping and
> + * visit the output string determined by *@obj.

Not covered: dealloc visitor.  It does nothing, of course.

Our current input / output visitors indeed parse from / unparse to a
string, and but isn't this a *visitor* detail that doesn't belong here?
We could theoretically do binary serialization with a pair of visitors,
where the output visitor writes the numeric encoding, and the input
visitor reads it back.

Suggest:

    Input visitors set *@obj according to their input.  Other visitors
    leave it unchanged.

> + */
>  void visit_type_enum(Visitor *v, const char *name, int *obj,
>                       const char *const strings[], Error **errp);
> +
> +/**
> + * Visit an integer value tied to @name in the current object visit.
> + * @name will be NULL if this is visited as part of a list.
> + * For input visitors, set *@obj to the parsed value; for other visitors,
> + * leave *@obj unchanged.

Here, your text is very close to what I suggested for enums :)

> + */
>  void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
> +/**
> + * Visit a uint8_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to uint8_t range.
> + */
>  void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
>                        Error **errp);
> +/**
> + * Visit a uint16_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to uint16_t range.
> + */
>  void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj,
>                         Error **errp);
> +/**
> + * Visit a uint32_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to uint32_t range.
> + */
>  void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
>                         Error **errp);
> +/**
> + * Visit a uint64_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to uint64_t range
> + * (that is, ensures it is unsigned).

I'd scratch the parenthesis.

> + */
>  void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
>                         Error **errp);
> +/**
> + * Visit an int8_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to int8_t range.
> + */
>  void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp);
> +/**
> + * Visit an int16_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to int16_t range.
> + */
>  void visit_type_int16(Visitor *v, const char *name, int16_t *obj,
>                        Error **errp);
> +/**
> + * Visit an int32_t value tied to @name in the current object visit.
> + * Like visit_type_int(), except clamps the value to int32_t range.
> + */
>  void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
>                        Error **errp);
> +/**
> + * Visit an int64_t value tied to @name in the current object visit.
> + * Identical to visit_type_int().
> + */
>  void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
>                        Error **errp);
> +/**
> + * Visit a uint64_t value tied to @name in the current object visit.
> + * Like visit_type_uint64(), except that some visitors may choose to
> + * recognize additional suffixes for easily scaling input values.

"use different syntax, say suffixes for scaling values."

> + */
>  void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
>                       Error **errp);
> +
> +/**
> + * Visit a boolean value tied to @name in the current object visit.
> + * @name will be NULL if this is visited as part of a list.
> + * Input visitors set *@obj to the value; other visitors will leave
> + * *@obj unchanged.

You're getting closer and closer to my version of this clause :)

> + */
>  void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
> +
> +/**
> + * Visit a string value tied to @name in the current object visit.
> + * @name will be NULL if this is visited as part of a list.
> + * @obj must be non-NULL.

Same for the other visit_type_T(), but not specified there.

>                             Input visitors set *@obj to the parsed
> + * string (never NULL); while output visitors leave *@obj unchanged,
> + * except that a NULL *@obj will be treated the same as "".

Suggest:

    Input visitors set *@obj according to their input (never NULL).
    Other visitors leave it unchanged.  They commonly treat NULL like "".

> + *
> + * FIXME: Unfortunately not const-correct for output visitors.

Is that fixable?

> + * FIXME: Callers that try to output NULL *obj should not be allowed.
> + */
>  void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
> +
> +/**
> + * Visit a number value tied to @name in the current object visit.
> + * @name will be NULL if this is visited as part of a list.
> + * Input visitors set *@obj to the value; other visitors will leave
> + * *@obj unchanged.
> + */
>  void visit_type_number(Visitor *v, const char *name, double *obj,
>                         Error **errp);
> +
> +/**
> + * Visit an arbitrary qtype value tied to @name in the current object visit.

Scratch qtype?

> + * @name will be NULL if this is visited as part of a list.
> + * Input visitors set *@obj to the value; other visitors will leave
> + * *@obj (which must not be NULL) unchanged.

Should specify that input visitors never set it to null.  Suggest:

    Input visitors set *@obj according to their input (never NULL).
    Other visitors leave it unchanged.  It must not be NULL then.

> + */
>  void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
> +
> +/**
> + * Mark the start of visiting the branches of a union. Return true if
> + * @data_present.

Not quite.  Actually returns true when the visitor doesn't provide this
callback, or else whatever its callback returns.  Only the dealloc
visitor provides it, and it returns @data_present.

You remove the function along with visit_end_union() in PATCH 32.  I'd
be okay with leaving them undocumented apart from the FIXME until then.
But if we add a contract now, it better be correct.

> + * FIXME: Should not be needed
> + */
>  bool visit_start_union(Visitor *v, bool data_present, Error **errp);
> +/**
> + * Mark the end of union branches, after visit_start_union().
> + * FIXME: Should not be needed
> + */
>  void visit_end_union(Visitor *v, bool data_present, Error **errp);
>
>  #endif
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 2d3743b..1612d0d 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -18,9 +18,21 @@
>  #include "qapi/visitor.h"
>  #include "qapi/visitor-impl.h"
>
> +/* Determine if this is an output visitor.
> + * Useful for making some tighter assertions that hold for output
> + * visitors, but not for input or dealloc visitors. */
> +static bool visit_is_output(Visitor *v)
> +{
> +    return v->type_enum == output_type_enum;

This is a hack.  As mentioned above, we could have an output visitor
doing binary serialization, which would need a different type_enum.  I'm
okay with the hack for now, but it needs a scary comment.

> +}
> +
>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp)
>  {
> +    assert(obj ? size : !size);
> +    if (obj && visit_is_output(v)) {
> +        assert(*obj);
> +    }

This needs to match the contract specified in visitor.h, but so far the
contract is too confusing for me to judge.  I'm skipping review of this
and the following assertion changes for now.

>      v->start_struct(v, name, obj, size, errp);
>  }
>
> @@ -32,6 +44,10 @@ void visit_end_struct(Visitor *v, Error **errp)
>  void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
>                                   Error **errp)
>  {
> +    assert(obj && size);
> +    if (visit_is_output(v)) {
> +        assert(*obj);
> +    }
>      if (v->start_implicit_struct) {
>          v->start_implicit_struct(v, obj, size, errp);
>      }
> @@ -51,6 +67,7 @@ void visit_start_list(Visitor *v, const char *name, Error **errp)
>
>  GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
>  {
> +    assert(list);
>      return v->next_list(v, list, errp);
>  }
>
> @@ -85,6 +102,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
>  void visit_get_next_type(Visitor *v, const char *name, QType *type,
>                           bool promote_int, Error **errp)
>  {
> +    assert(type);
>      if (v->get_next_type) {
>          v->get_next_type(v, name, type, promote_int, errp);
>      }
> @@ -93,11 +111,13 @@ void visit_get_next_type(Visitor *v, const char *name, QType *type,
>  void visit_type_enum(Visitor *v, const char *name, int *obj,
>                       const char *const strings[], Error **errp)
>  {
> +    assert(obj && strings);
>      v->type_enum(v, name, obj, strings, errp);
>  }
>
>  void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
>  {
> +    assert(obj);
>      v->type_int64(v, name, obj, errp);
>  }
>
> @@ -145,6 +165,7 @@ void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
>  void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
>                         Error **errp)
>  {
> +    assert(obj);
>      v->type_uint64(v, name, obj, errp);
>  }
>
> @@ -192,12 +213,14 @@ void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
>  void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
>                        Error **errp)
>  {
> +    assert(obj);
>      v->type_int64(v, name, obj, errp);
>  }
>
>  void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
>                       Error **errp)
>  {
> +    assert(obj);
>      if (v->type_size) {
>          v->type_size(v, name, obj, errp);
>      } else {
> @@ -207,22 +230,35 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
>
>  void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
>  {
> +    assert(obj);
>      v->type_bool(v, name, obj, errp);
>  }
>
>  void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>  {
> +    assert(obj);
> +    /* TODO: Fix callers to not pass NULL when they mean "", so that we
> +     * can enable:
> +    if (visit_is_output(v)) {
> +        assert(*obj);
> +    }
> +     */
>      v->type_str(v, name, obj, errp);
>  }
>
>  void visit_type_number(Visitor *v, const char *name, double *obj,
>                         Error **errp)
>  {
> +    assert(obj);
>      v->type_number(v, name, obj, errp);
>  }
>
>  void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
>  {
> +    assert(obj);
> +    if (visit_is_output(v)) {
> +        assert(*obj);
> +    }
>      v->type_any(v, name, obj, errp);
>  }
>
> @@ -233,7 +269,6 @@ void output_type_enum(Visitor *v, const char *name, int *obj,
>      int value = *obj;
>      char *enum_str;
>
> -    assert(strings);
>      while (strings[i++] != NULL);
>      if (value < 0 || value >= i - 1) {
>          error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null");
> @@ -251,8 +286,6 @@ void input_type_enum(Visitor *v, const char *name, int *obj,
>      int64_t value = 0;
>      char *enum_str;
>
> -    assert(strings);
> -
>      visit_type_str(v, name, &enum_str, &local_err);
>      if (local_err) {
>          error_propagate(errp, local_err);

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions
  2016-01-21 20:08   ` Markus Armbruster
@ 2016-01-22  0:30     ` Eric Blake
  2016-01-22 12:18       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-22  0:30 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 24392 bytes --]

On 01/21/2016 01:08 PM, Markus Armbruster wrote:
> All right, this one's a bear.  Not because the patch is bad, but because
> what it tries to do is bloody difficult.

Is there any reasonable split (such as adding some of the assertions in
earlier patches) that would make it any easier? Or do we just bite the
bullet and do it?

> 
> Eric Blake <eblake@redhat.com> writes:
> 
>> The visitor interface for mapping between QObject/QemuOpts/string
>> and qapi has formerly been documented only by reading source code,
> 
> Polite way to say "is scandalously undocumented".

Indeed.

> 
>> making it difficult to propose changes to either scripts/qapi*.py
>> or to clients without knowing whether those changes would be safe.
>> This adds documentation, including mentioning when parameters can
>> be NULL, and where there are still some interface warts that would
>> be nice to remove.  In particular, I have plans to remove
>> visit_start_union() in a future patch.
> 
> Suggest
> 
>     The visitor interface for mapping between QObject/QemuOpts/string
>     and QAPI is pretty much undocumented, making changes to visitor
>     core, individual visitors, and users of visitors difficult.
> 
>     Correct this by retrofitting proper contracts.  Document some
>     interface warts that would be nice to remove.  In particular, I have
>     plans to remove visit_start_union() in a future patch.

Your suggestions here, and elsewhere, are good and will be in my next
spin.  I'll trim to just the places where you have more than just a
wording suggestion.


>>  include/qapi/visitor-impl.h |  31 ++++++-
>>  include/qapi/visitor.h      | 196 ++++++++++++++++++++++++++++++++++++++++++++
>>  qapi/qapi-visit-core.c      |  39 ++++++++-
>>  3 files changed, 262 insertions(+), 4 deletions(-)
>>
> 
> My review probably makes more sense if you skip ahead to visitor.h, then
> come back here.

If I remember, I'll use -O when generating v10 to force visitor.h first,
other .h second, and .c last (I don't always remember to do it; maybe I
should add it into my handy .git alias that I use for firing off long
series).  [I really wish the git people would make it possible to
automate -O via 'git config', and to make it easier to have a
per-project preferred order file]

> 
>> +
>>  struct Visitor
>>  {
>> -    /* Must be set */
>> +    /* Must be provided to visit structs (the string visitors do not
>> +     * currently visit structs). */
> 
> Uh, the string visitors don't decide what gets visited, their users do.
> The string visitors don't support visiting structs.  The restriction
> needs to be documented, but this isn't the place to do it, is it?

Good point.  I think what I will do is split out a separate patch that
documents, per visitor with limitation, what callers cannot (yet) do
with that visitor.

>> +    /* May be NULL; most useful for input visitors. */
> 
> "Optional" would be a bit terser than "May be NULL".
> 
> Why is it "most useful for input visitors"?  For what it's worth, the
> dealloc visitor finds it useful, too...

and a later patch adds it to the QemuOpts input visitor (Zoltan's patch
has been sitting for how many months now?).  I'll come up with something.

> 
>>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
>>                                    Error **errp);
>>      /* May be NULL */
>>      void (*end_implicit_struct)(Visitor *v);
>>
>> +    /* Must be set */
>>      void (*start_list)(Visitor *v, const char *name, Error **errp);
>> +    /* Must be set */
>>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>>      /* Must be set */
>>      void (*end_list)(Visitor *v);
> 
> A visitor could omit these two with similar consequences to omitting
> start_struct() and end_struct(): attempts to visit lists crash then.  In
> fact, the string visitors omitted them until commit 659268f and 69e2556,
> respectively.

Which is why, as you pointed out, it may be better to document the
limitations in the string visitor rather than here, and in this file
maybe just mention at the top something along the lines that "must be
set" really means that "only needs to be set if your callers are
expecting a visit to encounter this type; the corresponding crash on
calling NULL is your hint to write missing functionality in your visitor".


>>      /* May be NULL; only needed for input visitors. */
>        void (*get_next_type)(Visitor *v, const char *name, QType *type,
>                              bool promote_int, Error **errp);
> 
> I figure it must be set for input visitors, because without it, the
> generated visit function will assume QTYPE_NONE, and fail.
> 
> Suggest "Must be set for input visitors."

Correct, and nice wording.

> 
> Looks like we say "must be set to visit this" when at least one visitor
> doesn't provide, and "must be set" when all our current visitors
> provide.  Hmm.
> 
>>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>>                       Error **errp);
>>
>>      /* May be NULL; most useful for input visitors. */
>>      void (*optional)(Visitor *v, const char *name, bool *present);
>>
>> +    /* FIXME - needs to be removed */
>>      bool (*start_union)(Visitor *v, bool data_present, Error **errp);
>> +    /* FIXME - needs to be removed */
>>      void (*end_union)(Visitor *v, bool data_present, Error **errp);
>>  };
>>
>> +/**
> 
> The /** is a marker for GTK-Doc.  We don't actually follow GTK-Doc
> conventions, however (they suck).  Sure we want the extra * anyway?

I don't mind dropping it.

>> +++ b/include/qapi/visitor.h
>> @@ -18,6 +18,20 @@
>>  #include "qapi/error.h"
>>  #include <stdlib.h>
>>
>> +/* This file describes the client view for visiting a map between
>> + * generated QAPI C structs and another representation (command line
>> + * options, strings, or QObjects).
> 
> "visiting a map"?

I'm a victim of too much rebasing.  I think I meant:

...the client view for mapping between generated QAPI C structs and
another representation...

> 
>>                                      An input visitor converts from
>> + * some other form into QAPI representation; an output visitor
>> + * converts from QAPI back into another form.
> 
> Let me try to work out what this visitor thingy is about.
> 
> The QAPI schema defines a set of QAPI types.  QAPI types can have
> members of QAPI type.  A value of such a type is actually a root of a
> graph of QAPI values.
> 
> QAPI generates visitor functions to walk these graphs.  They currently
> work only for trees, because that's all we need.
> 
> Walking a tree (or graph for that matter) is useful only to actually do
> something with the nodes.  The generated visitor functions do the
> walking and the visitor classes do the doing.  This is the visitor
> pattern at work: we factor walking the data structure out of the various
> tasks that need to walk it to do something.

Additionally, it is possible to use the visitor without a qapi struct,
purely for their side effects of translation to or from the alternative
representation of that visitor, by manually calling visitor functions in
the same order that generated QAPI structs would use.

> 
> Three kinds of visitor classes exist: two output visitors (QMP and
> string), three input visitors (QMP, string and QemuOpts), and the
> dealloc visitor.
> 
> With an output visitor, the visitor functions walk a tree, and the
> output visitor builds up some output.  QmpOutputVisitor builds a QObject
> matching QMP wire format,, and StringOutputVisitor builds a C string.
> Both convert a QAPI tree to another representation.

Or, when passed NULL obj, create the other representation out of thin
air from the manual visit.

> 
> Similarly with the QapiDeallocVisitor, except nothing gets built.
> Instead, the tree gets destroyed node by node.
> 
> With an input visitor, the visitor functions walk a tree as the input
> visitor constructs it.  QmpInputVisitor constructs it from a QObject
> matching QMP wire format, StringInputVisitor from a C string, and
> OptsVisitor from a QemuOpts.  All three convert from another
> representation to a QAPI tree.

Or, when passed NULL obj, parse the other representation via a manual
visit with no QAPI object involved.

> 
> The Qmp visitors and the dealloc visitor a general: they can walk /
> build any QAPI tree.
> 
> The string visitors and the QemuOpts visitor have restrictions: they can
> walk / build only a subset.

Yes.

>> +/**
>> + * Prepare to visit an object tied to key @name.
> 
> Not just any object, but a *struct*.  Suggest:
> 
>  * Start visiting struct value @obj.
> 
>> + * @name will be NULL if this is visited as part of a list.
> 
> I'm afraid reality is a bit messier.
> 
> If the visited object is a member of struct or union, @name is its
> member name.
> 
> If it's a member of a list, @name is null.

[side thread in earlier message about possibly using "name[0]" instead
of NULL, as a later improvement]

> 
> If it's a command argument, @name is its parameter name.

But this is a special case of "visiting as part of a struct", since we
have a (possibly-implicit) QAPI struct for parameters.

> 
> If it's a command's result, @name is a meaningless string (qmp-marshal.c
> currently uses "unused").

But this is a special case of a root visit (the command result is the
top of a visit, so @name is meaningless).

> 
> Else, @name can be a meaningless string or null.

And this sentence is reached only for a root visit.  So now I'm thinking
along these lines:

As the first object in a visit (the root of a QAPI struct), @name is
meaningless. It is typically set to NULL or a descriptive string,
although some callers pass "unused".

At all other points of the visit, @name reflects the relationship of
this visit to the parent.  Either the visited object is a member of a
struct or union, and @name is its member name; or the visited object is
the member of a list and @name is NULL.

> 
> Same for other visit functions.

Is it okay to write it out once, and then have all other functions refer
back to the common location?

> 
>>                                                               The
>> + * caller then makes a series of visit calls for each key expected in
>> + * the object, where those visits set their respective obj parameter
>> + * to the address of a member of the qapi struct, and follows
>> + * everything by a call to visit_end_struct() to clean up resources.
> 
> I'd explain intended use after the parameters.
> 
>> + *
>> + * @obj can be NULL (in which case @size should also be 0) to indicate
> 
> "must be 0", because you assert that below.
> 
> Actually, I find this @size contract weird.  Passing the type's actual
> size would make at least as much sense as passing 0.  We generally pass
> 0 when the size isn't used, but that's just because it's the easiest
> value to pass.

We pass 0 precisely when @obj is NULL because that is the usage pattern
where we do NOT have a QAPI struct, but are manually using the visitor
for its side effects.  It's hard to have a non-zero size of a
non-existent struct.

> 
>> + * that there is no qapi C struct, and that the upcoming visit calls
>> + * are parsing input to or creating output from some other
>> + * representation.
> 
> This is very vague.
> 
> Can you point me to code passing null @obj?

Easiest example: hw/ppc/spapr_drc.c.  Maybe I should follow danpb's lead
and actually put some <example> usage of the visitors in the comments.

> 
> I suspect only some visitors accept null @obj.  Which ones?

I didn't check that; will do.

> 
>> + *
>> + * If @obj is not NULL, then input visitors malloc a qapi struct of
>> + * @size bytes into *@obj on success, and output visitors expect *@obj
>> + * to be a fully-populated qapi struct.
> 
> Only input visitors and the dealloc visitor use @obj.  The dealloc
> visitor doesn't need it in start_struct(), but has to save it for
> end_struct(), because that one doesn't get it passed.  Aside: feels
> stupid.

Probably possible to change, although none of my patches do it yet.

> 
> Only input visitors use @size, and they use it only when @obj isn't
> null.
> 
> We could make visitors check the member pointers passed to the member
> visits actually point into (@obj, @size).  Then *all* visitors would use
> @obj and @size.  I'm not asking for that to be done, I'm just providing
> an argument for a tighter contract.  The simplest one would be "Some
> visitors permit @obj to be null.  @size must be the struct value's
> size."  Except that doesn't match existing usage.  Unless we change it
> to match, we need to hedge "except @size is unused and may be anything
> when @obj is null", or "except @size is unused and must be zero when
> @obj is null".

My intent was the latter - @size is unused and must be zero when @obj is
NULL.  Also, rather than making every visitor track that @obj for the
current visit lies within (@obj, @size) of the parent struct, I'm
wondering if it would be better to do that tracking at the
qapi-visit-core level - but then we'd have two levels of code tracking a
stack of information instead of one.

> 
> This method is an awful mess.  Probably because it's serving quite
> different purposes in the different visitors.

Indeed.  visit_type_str() is the best example of the conflict of
interest between input and output visitors, in that we can't be
const-correct.

At one point, I was half-tempted to split input and output visitors into
two separate contracts, rather than trying to merge both types of visits
through a single interface and having the interface become a bit
unwieldy.  But as currently written, it's kind of convenient that a
single function in generated qapi-visit.c can handle all the visitors.


> 
>> + *
>> + * Set *@errp on failure; for example, if the input stream does not
>> + * have a member @name or if the member is not an object.
> 
> What do you mean by "if the member is not an object"?

s/object/struct/

If I'm using the QMP input visitor to parse the string "{ \"a\": 1 }",
and call visit_start_struct("a"), I expect an error because "1" is not a
struct.

> 
> Any other ways to fail?

I don't think so.

> 
> This is the only function comment that says anything about @errp.
> Should we document the various failure modes everywhere?

Probably useful.  More work, but worth doing.

> 
>> + *
>> + * FIXME: For input visitors, *@obj can be assigned here even if later
>> + * visits will fail; this can lead to memory leaks if clients aren't
>> + * careful.
> 
> Why is this a FIXME?  How could it be fixed?  More of the same below.

Fixed by 35/37, where we change the return type here, and fix all the
visit_type_FOO() functions to use that return type to properly use the
dealloc visitor on any partial *@obj, so that the callers of
visit_type_FOO() no longer have to worry about partial allocation.

Maybe my wording could be improved here.

> 
> My attempt at explaining intended use:
> 
>     After visit_start_struct() succeeds, the caller may visit its
>     members one after the other, passing the member's name and address
>     within the struct.  Finally, visit_end_struct() needs to be called
>     to clean up.
> 
> I guess what the reader really needs here to understand intended use is
> example code.  qapi-code-gen.txt has some.  Add a pointer?

Hmm, a pointer to qapi-code-gen would be shorter than an inline
<example> blurb.  But it only lists the generated usage; I may also want
to document the no-QAPI-struct usage (our friend spapr_drc.c again).

> 
>> + */
>>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>>                          size_t size, Error **errp);
> 
> Please separate each commented declaration with a blank line.  Not
> flagging further instances.

I was trying to group related functions; will switch to one blank in
related sets, and two blanks between sets (instead of zero/one).

> 
>> +/**
>> + * Complete a struct visit started earlier.
>> + * Must be called after any successful use of visit_start_struct(),
>> + * even if intermediate processing was skipped due to errors, to allow
>> + * the backend to release any resources.
> 
> Actually, destroying the visitor is safe even without calling the
> remaining visit_end_struct().  If we introduce a visitor reset as
> discussed elsewhere, that could probably be made safe, too.

Except that patch 33/37 asserts that destroying the visitor is only ever
done after all visit_end_struct()s have been paired, as a tighter
contract which will then let us free up some resources during the
end_struct() without having to track them for a potentially-early
visitor destruction.

And yes, I already have a followup series posted that introduces a
visitor reset, at least for the QMP output visitor (I wasn't sure
whether to generalize it to all visitors).


>> +/**
>> + * Prepare to visit an implicit struct.
>> + * Similar to visit_start_struct(), except that an implicit struct
>> + * represents a subset of keys that are present at the same nesting level
>> + * of a common object but as a separate qapi C struct, rather than a new
>> + * object at a deeper nesting level.
> 
> I'm afraid this is impenetrable.
> 
> I tried to refresh my memory on visit_start_implicit_struct()'s purpose
> by reading code, but that's pretty impenetrable, too.

Consider the input string { "a":1, "b":2 }.

With a normal QAPI struct, all fields of the JSON object are part of the
same C struct:
Foo { int a; int b; };

and it is visited with:
visit_start_struct(); visit_type_int("a", &a); visit_type_int("b", &b);
visit_end_struct();

But with an implicit struct (namely, a branch of a QAPI union type or a
member of a QAPI alternate type; we used to also do it for base classes
but changed that to inline fields instead), we have more than one C
struct that map to the same flat JSON object:
Bar { int b; };
Foo { int a; Bar *sub; }

which is visited with:
visit_start_struct(); visit_type_int("a", &b);
visit_start_implicit_struct(&sub); visit_type_int("b", &sub.b);
visit_end_implicit_struct(); visit_end_struct();

Suggestions on how to better word it are welcome.  I'm basically trying
to describe that this function marks the start of a new C struct used as
a sub-object while still conceptually parsing the same top-level QAPI
struct.

> 
>> + *
>> + * @obj must not be NULL, since this function is only called when
>> + * visiting with qapi structs.
> 
> Really?  Why does qmp_input_start_implicit_struct() check for null then?

Probably worth an independent cleanup.  The assertions added in this
patch prove that that check is dead.


>> +/**
>> + * Prepare to visit a list tied to an object key @name.
>> + * @name will be NULL if this is visited as part of another list.
>> + * After calling this, the elements must be collected until
>> + * visit_next_list() returns NULL, then visit_end_list() must be
>> + * used to complete the visit.
> 
> I'm afraid this doesn't really tell me how to visit a list.  Perhaps:
> 
>     After visit_start_list() succeeds, the caller may visit its members
>     one after the other, passing the value of visit_next_list() as @obj,
>     until visit_next_list() returns NULL.  Finally, visit_end_list()
>     needs to be called to clean up.
> 
> May want to add a pointer to example code in qapi-code-gen.txt.

And it changes again in 34/37.

We have two styles; our generated code, which is roughly:

visit_start_list(&obj);
while (visit_next_list()) {
    visit_type_FOO(element);
}
visit_end_list()

and the manual style of our friend hw/ppc/spapr_drc.c:

visit_start_list(NULL);
while (some other way to decide if data available) {
    visit_type_FOO(element);
}
visit_end_list()

That is, the use of visit_next_list() is optional depending on whether a
QAPI list is in use, but the visit_type_FOO() per element is necessary.
 We'll see if I can get the next wording attempt a bit easier to understand.

> 
>> + */
>>  void visit_start_list(Visitor *v, const char *name, Error **errp);
>> +/**

>> + *
>> + * Note that for some visitors (qapi-dealloc and qmp-output), when a
>> + * qapi GenericList linked list is not being used (comparable to when
>> + * a NULL obj is used for visit_start_struct()), it is acceptable to
>> + * bypass the use of visit_next_list() and just directly call the
>> + * appropriate visit_type_*() for each element in between the
>> + * visit_start_list() and visit_end_list() calls.
> 
> I'm confused.  Can you point me to code bypassing visit_next_list()?

See above; spapr_drc.c.


>>  /**
>     * Check if an optional member @name of an object needs visiting.
>     * For input visitors, set *@present according to whether the
>     * corresponding visit_type_*() needs calling; for other visitors,
>     * leave *@present unchanged.  Return *@present for convenience.
>    bool visit_optional(Visitor *v, const char *name, bool *present);
> 
> 
> Suggest to clarify:
> 
>     Does optional struct member @name need visiting?
>     @present points to the optional member's has_ flag.
>     Input visitors set *@present according to their input.  Other
>     visitors leave it unchanged.
>     In either case, return *@present.

Thanks. I originally added all the docs in one pass, but some of the
docs snuck in via earlier patches during rebase churn, so reviewing it
again here helps.

>> +/**
>> + * Visit a string value tied to @name in the current object visit.
>> + * @name will be NULL if this is visited as part of a list.
>> + * @obj must be non-NULL.
> 
> Same for the other visit_type_T(), but not specified there.
> 
>>                             Input visitors set *@obj to the parsed
>> + * string (never NULL); while output visitors leave *@obj unchanged,
>> + * except that a NULL *@obj will be treated the same as "".
> 
> Suggest:
> 
>     Input visitors set *@obj according to their input (never NULL).
>     Other visitors leave it unchanged.  They commonly treat NULL like "".
> 
>> + *
>> + * FIXME: Unfortunately not const-correct for output visitors.
> 
> Is that fixable?

Not easily, and certainly not as part of this series.  The best I can
think of is either two interfaces:

char *visit_input_type_str(Visitor *v, const char *name, Error **errp);
void visit_output_type_str(Visitor *v, const char *name, const char
*value, Error **errp);

used as:

obj.str = visit_input_type_str(v, "foo", &err);
visit_output_type_str(v, "foo", obj.str, &err);

or to make the single interface have more parameters:

void visit_type_str(Visitor *v, const char *name, const char *value_in,
char **value_out, Error **errp);

used as:

visit_type_str(v, "foo", obj.str, &obj.str, &err);

where input visits could pass NULL instead of value_in, and output
visits could pass NULL instead of value_out.

But either way, consistency would then argue that ALL visit_type_FOO()
interfaces should either have two forms (one for input, one for output)
or have more parameters (with input distinct from output), and it would
allow const-correctness on output visits of more than just strings.  And
which form would a dealloc visitor use?


>> +/**
>> + * Mark the start of visiting the branches of a union. Return true if
>> + * @data_present.
> 
> Not quite.  Actually returns true when the visitor doesn't provide this
> callback, or else whatever its callback returns.  Only the dealloc
> visitor provides it, and it returns @data_present.
> 
> You remove the function along with visit_end_union() in PATCH 32.  I'd
> be okay with leaving them undocumented apart from the FIXME until then.
> But if we add a contract now, it better be correct.

I like the 'FIXME'-only approach.


-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions
  2016-01-22  0:30     ` Eric Blake
@ 2016-01-22 12:18       ` Markus Armbruster
  2016-02-10  0:23         ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-22 12:18 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/21/2016 01:08 PM, Markus Armbruster wrote:
>> All right, this one's a bear.  Not because the patch is bad, but because
>> what it tries to do is bloody difficult.
>
> Is there any reasonable split (such as adding some of the assertions in
> earlier patches) that would make it any easier? Or do we just bite the
> bullet and do it?

I think the difficulty is in finding a contract that fits the current
code and makes sense.

Assertions and contract overlap: assertions partly enforce the contract.
Separating the two won't help.

We could try to split along sub-interface boundaries, say scalars,
structs, unions, alternates, lists.  Several smaller patches, but to
ensure overall consistency, you have to mentally merge them again.
Doesn't seem helpful.

Let's bite the bullet.

>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> The visitor interface for mapping between QObject/QemuOpts/string
>>> and qapi has formerly been documented only by reading source code,
>> 
>> Polite way to say "is scandalously undocumented".
>
> Indeed.
>
>> 
>>> making it difficult to propose changes to either scripts/qapi*.py
>>> or to clients without knowing whether those changes would be safe.
>>> This adds documentation, including mentioning when parameters can
>>> be NULL, and where there are still some interface warts that would
>>> be nice to remove.  In particular, I have plans to remove
>>> visit_start_union() in a future patch.
>> 
>> Suggest
>> 
>>     The visitor interface for mapping between QObject/QemuOpts/string
>>     and QAPI is pretty much undocumented, making changes to visitor
>>     core, individual visitors, and users of visitors difficult.
>> 
>>     Correct this by retrofitting proper contracts.  Document some
>>     interface warts that would be nice to remove.  In particular, I have
>>     plans to remove visit_start_union() in a future patch.
>
> Your suggestions here, and elsewhere, are good and will be in my next
> spin.  I'll trim to just the places where you have more than just a
> wording suggestion.
>
>
>>>  include/qapi/visitor-impl.h |  31 ++++++-
>>>  include/qapi/visitor.h      | 196 ++++++++++++++++++++++++++++++++++++++++++++
>>>  qapi/qapi-visit-core.c      |  39 ++++++++-
>>>  3 files changed, 262 insertions(+), 4 deletions(-)
>>>
>> 
>> My review probably makes more sense if you skip ahead to visitor.h, then
>> come back here.
>
> If I remember, I'll use -O when generating v10 to force visitor.h first,
> other .h second, and .c last (I don't always remember to do it; maybe I
> should add it into my handy .git alias that I use for firing off long
> series).  [I really wish the git people would make it possible to
> automate -O via 'git config', and to make it easier to have a
> per-project preferred order file]
>
>> 
>>> +
>>>  struct Visitor
>>>  {
>>> -    /* Must be set */
>>> +    /* Must be provided to visit structs (the string visitors do not
>>> +     * currently visit structs). */
>> 
>> Uh, the string visitors don't decide what gets visited, their users do.
>> The string visitors don't support visiting structs.  The restriction
>> needs to be documented, but this isn't the place to do it, is it?
>
> Good point.  I think what I will do is split out a separate patch that
> documents, per visitor with limitation, what callers cannot (yet) do
> with that visitor.

Makes sense.

>>> +    /* May be NULL; most useful for input visitors. */
>> 
>> "Optional" would be a bit terser than "May be NULL".
>> 
>> Why is it "most useful for input visitors"?  For what it's worth, the
>> dealloc visitor finds it useful, too...
>
> and a later patch adds it to the QemuOpts input visitor (Zoltan's patch
> has been sitting for how many months now?).  I'll come up with something.
>
>> 
>>>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
>>>                                    Error **errp);
>>>      /* May be NULL */
>>>      void (*end_implicit_struct)(Visitor *v);
>>>
>>> +    /* Must be set */
>>>      void (*start_list)(Visitor *v, const char *name, Error **errp);
>>> +    /* Must be set */
>>>      GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
>>>      /* Must be set */
>>>      void (*end_list)(Visitor *v);
>> 
>> A visitor could omit these two with similar consequences to omitting
>> start_struct() and end_struct(): attempts to visit lists crash then.  In
>> fact, the string visitors omitted them until commit 659268f and 69e2556,
>> respectively.
>
> Which is why, as you pointed out, it may be better to document the
> limitations in the string visitor rather than here, and in this file
> maybe just mention at the top something along the lines that "must be
> set" really means that "only needs to be set if your callers are
> expecting a visit to encounter this type; the corresponding crash on
> calling NULL is your hint to write missing functionality in your visitor".

Good idea.

Perhaps say "Must be set for $TYPE visits to work" in each applicable
place, and explain what that means just once.

>>>      /* May be NULL; only needed for input visitors. */
>>        void (*get_next_type)(Visitor *v, const char *name, QType *type,
>>                              bool promote_int, Error **errp);
>> 
>> I figure it must be set for input visitors, because without it, the
>> generated visit function will assume QTYPE_NONE, and fail.
>> 
>> Suggest "Must be set for input visitors."
>
> Correct, and nice wording.
>
>> 
>> Looks like we say "must be set to visit this" when at least one visitor
>> doesn't provide, and "must be set" when all our current visitors
>> provide.  Hmm.
>> 
>>>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>>>                       Error **errp);
>>>
>>>      /* May be NULL; most useful for input visitors. */
>>>      void (*optional)(Visitor *v, const char *name, bool *present);
>>>
>>> +    /* FIXME - needs to be removed */
>>>      bool (*start_union)(Visitor *v, bool data_present, Error **errp);
>>> +    /* FIXME - needs to be removed */
>>>      void (*end_union)(Visitor *v, bool data_present, Error **errp);
>>>  };
>>>
>>> +/**
>> 
>> The /** is a marker for GTK-Doc.  We don't actually follow GTK-Doc
>> conventions, however (they suck).  Sure we want the extra * anyway?
>
> I don't mind dropping it.

Please do.

>>> +++ b/include/qapi/visitor.h
>>> @@ -18,6 +18,20 @@
>>>  #include "qapi/error.h"
>>>  #include <stdlib.h>
>>>
>>> +/* This file describes the client view for visiting a map between
>>> + * generated QAPI C structs and another representation (command line
>>> + * options, strings, or QObjects).
>> 
>> "visiting a map"?
>
> I'm a victim of too much rebasing.  I think I meant:
>
> ...the client view for mapping between generated QAPI C structs and
> another representation...

That makes more sense.  It's rather terse, though.  I explained the
purpose and structur more verbosely below.  Perhaps we can and adapt
that into an introductory comment.  But let's figure out the contract
before we worry about the introduction.

>>>                                      An input visitor converts from
>>> + * some other form into QAPI representation; an output visitor
>>> + * converts from QAPI back into another form.
>> 
>> Let me try to work out what this visitor thingy is about.
>> 
>> The QAPI schema defines a set of QAPI types.  QAPI types can have
>> members of QAPI type.  A value of such a type is actually a root of a
>> graph of QAPI values.
>> 
>> QAPI generates visitor functions to walk these graphs.  They currently
>> work only for trees, because that's all we need.
>> 
>> Walking a tree (or graph for that matter) is useful only to actually do
>> something with the nodes.  The generated visitor functions do the
>> walking and the visitor classes do the doing.  This is the visitor
>> pattern at work: we factor walking the data structure out of the various
>> tasks that need to walk it to do something.
>
> Additionally, it is possible to use the visitor without a qapi struct,
> purely for their side effects of translation to or from the alternative
> representation of that visitor, by manually calling visitor functions in
> the same order that generated QAPI structs would use.

This is like walking a virtual tree.

Virtual walks add complexity:

* When we walk a real tree, we pass real @obj pointers.  When we walk a
  virtual tree, we pass null pointers.

  Example: we pass null to visit_start_struct() when we have no real
  QAPI struct.  However, we *can't* pass null to visits of scalars like
  visit_type_int().  Scalars must be real.

  When exactly are null pointers okay?  Or in other words, which parts
  of the tree may be virtual?  Needs to be spelled out.

  Does "may be virtual" depend on the actual visitor?

* When we walk a real tree, we use in-tree information to find optional
  stuff: visit_optional() for optional members, visit_next_list() for
  remaining list members, visit_get_next_type() to help find the
  alternate member in use.  When we walk a virtual tree, we make those
  decisions ourselves.

* Anything else?

Not documenting interfaces breeds interface complexity.

Related complication: the generated visitor functions accept
half-constructed trees, because we need to be able to destroy such trees
with the dealloc visitor.  If you visit a half-constructed tree with an
output visitor, the visit function doesn't flag the misuse, and the
output will be garbage.

>> Three kinds of visitor classes exist: two output visitors (QMP and
>> string), three input visitors (QMP, string and QemuOpts), and the
>> dealloc visitor.
>> 
>> With an output visitor, the visitor functions walk a tree, and the
>> output visitor builds up some output.  QmpOutputVisitor builds a QObject
>> matching QMP wire format,, and StringOutputVisitor builds a C string.
>> Both convert a QAPI tree to another representation.
>
> Or, when passed NULL obj, create the other representation out of thin
> air from the manual visit.

Mind, I'm talking about the generated visitor

With an output visitor, the visitor functions walk a tree (real or
virtual), and...

>> Similarly with the QapiDeallocVisitor, except nothing gets built.
>> Instead, the tree gets destroyed node by node.
>> 
>> With an input visitor, the visitor functions walk a tree as the input
>> visitor constructs it.  QmpInputVisitor constructs it from a QObject
>> matching QMP wire format, StringInputVisitor from a C string, and
>> OptsVisitor from a QemuOpts.  All three convert from another
>> representation to a QAPI tree.
>
> Or, when passed NULL obj, parse the other representation via a manual
> visit with no QAPI object involved.

Actually, the generated visitor functions assume real trees.  Possibly
half-constructed ones (see "related complication" above).  Null should
only occur in a half-constructed tree.  The generated visitor functions
then skip over that part.

To walk the virtual part of a tree, you need to stitch together calls of
the visitor.h interface yourself.  I guess that's what you call "manual
visit".

I think the introduction should explain visiting real trees first, and
virtual trees second, to keep complexity under control.

>> The Qmp visitors and the dealloc visitor a general: they can walk /
>> build any QAPI tree.
>> 
>> The string visitors and the QemuOpts visitor have restrictions: they can
>> walk / build only a subset.
>
> Yes.
>
>>> +/**
>>> + * Prepare to visit an object tied to key @name.
>> 
>> Not just any object, but a *struct*.  Suggest:
>> 
>>  * Start visiting struct value @obj.
>> 
>>> + * @name will be NULL if this is visited as part of a list.
>> 
>> I'm afraid reality is a bit messier.
>> 
>> If the visited object is a member of struct or union, @name is its
>> member name.
>> 
>> If it's a member of a list, @name is null.
>
> [side thread in earlier message about possibly using "name[0]" instead
> of NULL, as a later improvement]
>
>> 
>> If it's a command argument, @name is its parameter name.
>
> But this is a special case of "visiting as part of a struct", since we
> have a (possibly-implicit) QAPI struct for parameters.

Conceptually, yes.  In actual code, the struct need not exist, and then
the name comes from different code, which could result in different
@name.  That's why I made it a separate clause.  Turns out @name is the
same, i.e. we didn't screw that one up.

>> If it's a command's result, @name is a meaningless string (qmp-marshal.c
>> currently uses "unused").
>
> But this is a special case of a root visit (the command result is the
> top of a visit, so @name is meaningless).

In actual code, this is a differnet root visit, and it actually does
things differently: "unused" rather than NULL.  The difference is of
course pointless.

>> Else, @name can be a meaningless string or null.
>
> And this sentence is reached only for a root visit.

Unless I missed a case.  I wasn't quite sure, so I used a catch-all
clause.

>                                                      So now I'm thinking
> along these lines:
>
> As the first object in a visit (the root of a QAPI struct), @name is
> meaningless. It is typically set to NULL or a descriptive string,
> although some callers pass "unused".

Scratch "or a descriptive string".

> At all other points of the visit, @name reflects the relationship of
> this visit to the parent.  Either the visited object is a member of a
> struct or union,

alternate?

>                  and @name is its member name; or the visited object is
> the member of a list and @name is NULL.

Works for me.

We'll likely have to change it to get sane error messages, but let's not
worry about that now.

>> Same for other visit functions.
>
> Is it okay to write it out once, and then have all other functions refer
> back to the common location?

Yes, please.

>>>                                                               The
>>> + * caller then makes a series of visit calls for each key expected in
>>> + * the object, where those visits set their respective obj parameter
>>> + * to the address of a member of the qapi struct, and follows
>>> + * everything by a call to visit_end_struct() to clean up resources.
>> 
>> I'd explain intended use after the parameters.
>> 
>>> + *
>>> + * @obj can be NULL (in which case @size should also be 0) to indicate
>> 
>> "must be 0", because you assert that below.
>> 
>> Actually, I find this @size contract weird.  Passing the type's actual
>> size would make at least as much sense as passing 0.  We generally pass
>> 0 when the size isn't used, but that's just because it's the easiest
>> value to pass.
>
> We pass 0 precisely when @obj is NULL because that is the usage pattern
> where we do NOT have a QAPI struct, but are manually using the visitor
> for its side effects.  It's hard to have a non-zero size of a
> non-existent struct.

"I don't have a struct, and therefore I don't have a size, and therefore
the size must be zero" isn't exactly impeccable logic, but defensible.

Equally defensible, at least for me: "my struct is virtual, and its size
is the same as if it was real".

Why constrain @size when it's not used?  Just document that it's not
used.

>>> + * that there is no qapi C struct, and that the upcoming visit calls
>>> + * are parsing input to or creating output from some other
>>> + * representation.
>> 
>> This is very vague.
>> 
>> Can you point me to code passing null @obj?
>
> Easiest example: hw/ppc/spapr_drc.c.  Maybe I should follow danpb's lead
> and actually put some <example> usage of the visitors in the comments.
>
>> I suspect only some visitors accept null @obj.  Which ones?
>
> I didn't check that; will do.
>
>> 
>>> + *
>>> + * If @obj is not NULL, then input visitors malloc a qapi struct of
>>> + * @size bytes into *@obj on success, and output visitors expect *@obj
>>> + * to be a fully-populated qapi struct.
>> 
>> Only input visitors and the dealloc visitor use @obj.  The dealloc
>> visitor doesn't need it in start_struct(), but has to save it for
>> end_struct(), because that one doesn't get it passed.  Aside: feels
>> stupid.
>
> Probably possible to change, although none of my patches do it yet.

Please think twice before from growing the QAPI patch queue further.  We
really, really need to clear it.  A TODO comment would be welcome,
though.

>> Only input visitors use @size, and they use it only when @obj isn't
>> null.
>> 
>> We could make visitors check the member pointers passed to the member
>> visits actually point into (@obj, @size).  Then *all* visitors would use
>> @obj and @size.  I'm not asking for that to be done, I'm just providing
>> an argument for a tighter contract.  The simplest one would be "Some
>> visitors permit @obj to be null.  @size must be the struct value's
>> size."  Except that doesn't match existing usage.  Unless we change it
>> to match, we need to hedge "except @size is unused and may be anything
>> when @obj is null", or "except @size is unused and must be zero when
>> @obj is null".
>
> My intent was the latter - @size is unused and must be zero when @obj is
> NULL.  Also, rather than making every visitor track that @obj for the
> current visit lies within (@obj, @size) of the parent struct, I'm
> wondering if it would be better to do that tracking at the
> qapi-visit-core level - but then we'd have two levels of code tracking a
> stack of information instead of one.

I don't think we should do this tracking now.  This is just about
figuring out a sensible contract without undue clinging to what the
current code does.

>> This method is an awful mess.  Probably because it's serving quite
>> different purposes in the different visitors.
>
> Indeed.  visit_type_str() is the best example of the conflict of
> interest between input and output visitors, in that we can't be
> const-correct.
>
> At one point, I was half-tempted to split input and output visitors into
> two separate contracts, rather than trying to merge both types of visits
> through a single interface and having the interface become a bit
> unwieldy.  But as currently written, it's kind of convenient that a
> single function in generated qapi-visit.c can handle all the visitors.

It's a tradeoff.  The generated visitor code is repetitive enough as it
is.

>>> + *
>>> + * Set *@errp on failure; for example, if the input stream does not
>>> + * have a member @name or if the member is not an object.
>> 
>> What do you mean by "if the member is not an object"?
>
> s/object/struct/
>
> If I'm using the QMP input visitor to parse the string "{ \"a\": 1 }",
> and call visit_start_struct("a"), I expect an error because "1" is not a
> struct.
>
>> 
>> Any other ways to fail?
>
> I don't think so.
>
>> 
>> This is the only function comment that says anything about @errp.
>> Should we document the various failure modes everywhere?
>
> Probably useful.  More work, but worth doing.

I think documenting the failure modes is something we could split off
this patch.

>>> + *
>>> + * FIXME: For input visitors, *@obj can be assigned here even if later
>>> + * visits will fail; this can lead to memory leaks if clients aren't
>>> + * careful.
>> 
>> Why is this a FIXME?  How could it be fixed?  More of the same below.
>
> Fixed by 35/37, where we change the return type here, and fix all the
> visit_type_FOO() functions to use that return type to properly use the
> dealloc visitor on any partial *@obj, so that the callers of
> visit_type_FOO() no longer have to worry about partial allocation.
>
> Maybe my wording could be improved here.

>From the current wording, I gather the interface is needlessly hard to
use correctly, but I don't understand how exactly users can screw up.  I
hope I will after review of PATCH 35.  Perhaps I can suggest clearer
wording then.

>> My attempt at explaining intended use:
>> 
>>     After visit_start_struct() succeeds, the caller may visit its
>>     members one after the other, passing the member's name and address
>>     within the struct.  Finally, visit_end_struct() needs to be called
>>     to clean up.
>> 
>> I guess what the reader really needs here to understand intended use is
>> example code.  qapi-code-gen.txt has some.  Add a pointer?
>
> Hmm, a pointer to qapi-code-gen would be shorter than an inline
> <example> blurb.  But it only lists the generated usage; I may also want
> to document the no-QAPI-struct usage (our friend spapr_drc.c again).

That's an argument against doing it in qapi-code-gen.txt, because
walking virtual trees is probably out of scope there.

>>> + */
>>>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>>>                          size_t size, Error **errp);
>> 
>> Please separate each commented declaration with a blank line.  Not
>> flagging further instances.
>
> I was trying to group related functions; will switch to one blank in
> related sets, and two blanks between sets (instead of zero/one).
>
>> 
>>> +/**
>>> + * Complete a struct visit started earlier.
>>> + * Must be called after any successful use of visit_start_struct(),
>>> + * even if intermediate processing was skipped due to errors, to allow
>>> + * the backend to release any resources.
>> 
>> Actually, destroying the visitor is safe even without calling the
>> remaining visit_end_struct().  If we introduce a visitor reset as
>> discussed elsewhere, that could probably be made safe, too.
>
> Except that patch 33/37 asserts that destroying the visitor is only ever
> done after all visit_end_struct()s have been paired, as a tighter
> contract which will then let us free up some resources during the
> end_struct() without having to track them for a potentially-early
> visitor destruction.

Requiring all outstanding end_FOO()s to be called before you may destroy
or reset makes the interface harder to use.  Moreover, not being able to
destroy at any time is unusual.  I can accept this if it makes the code
substantially simpler.

> And yes, I already have a followup series posted that introduces a
> visitor reset, at least for the QMP output visitor (I wasn't sure
> whether to generalize it to all visitors).
>
>
>>> +/**
>>> + * Prepare to visit an implicit struct.
>>> + * Similar to visit_start_struct(), except that an implicit struct
>>> + * represents a subset of keys that are present at the same nesting level
>>> + * of a common object but as a separate qapi C struct, rather than a new
>>> + * object at a deeper nesting level.
>> 
>> I'm afraid this is impenetrable.
>> 
>> I tried to refresh my memory on visit_start_implicit_struct()'s purpose
>> by reading code, but that's pretty impenetrable, too.
>
> Consider the input string { "a":1, "b":2 }.
>
> With a normal QAPI struct, all fields of the JSON object are part of the
> same C struct:
> Foo { int a; int b; };
>
> and it is visited with:
> visit_start_struct(); visit_type_int("a", &a); visit_type_int("b", &b);
> visit_end_struct();
>
> But with an implicit struct (namely, a branch of a QAPI union type or a
> member of a QAPI alternate type; we used to also do it for base classes
> but changed that to inline fields instead), we have more than one C
> struct that map to the same flat JSON object:
> Bar { int b; };
> Foo { int a; Bar *sub; }
>
> which is visited with:
> visit_start_struct(); visit_type_int("a", &b);
> visit_start_implicit_struct(&sub); visit_type_int("b", &sub.b);
> visit_end_implicit_struct(); visit_end_struct();
>
> Suggestions on how to better word it are welcome.  I'm basically trying
> to describe that this function marks the start of a new C struct used as
> a sub-object while still conceptually parsing the same top-level QAPI
> struct.

Thinking aloud...

QAPI defines both C data types and a QMP wire format.

The work of mapping between the two is split between generated visitor
functions and the QMP visitors.  Roughly, the visitor functions direct
the mapping of the tree structure, and the QMP visitors take care of the
leaves.

The QAPI tree structure and the QMP tree structure are roughly the same.
Exception: some structs are inlined into a parent struct in QMP, but
stored as a separate, boxed struct in QAPI.  We call them "implicit"
structs.  We got rid of one case (base structs), but we still got two
(flat union and alternate members).  I dislike the exception, but we
need to document what we've got.

It's mostly handled by the visitor functions, but two visitors need to
hook into their handling, because they allocate / free the boxed struct:
the QMP input visitor and the dealloc visitor.

The QMP output visitor doesn't.  The effect is that the members of the
implicit struct get inlined into the explicit struct that surrounds it.

I oversimplified when I said "and a QMP wire format": since we acquired
string and QemuOpts visitors, we also have a string and QemuOpts format.
Both are partial: they don't support all of QAPI.

However, these formats aren't independent of the QMP wire format.  Since
we use the same visitor functions, and the visitor functions map the
tree structure, the tree structure gets mapped just like for QMP.
Almost theoretical, because these visitors don't support most of the
structure.

If we wanted a format that doesn't inline implicit structs, the visitor
would want to implement visit_start_implicit_struct() and
visit_end_implicit_struct() just like visit_start_struct() and
visit_end_struct().  Differences:

* start_implicit_struct() lacks the @name parameter.

* end_implicit_struct() lacks the @errp now.  Later in this series, this
  becomes: there is no check_implicit_struct().

Not inlining implicit structs seems impossible with the current API.
I'm *not* asking for a change that makes it possible.  Instead, my point
is: the inlining of implicit structs is baked into the visitor
interfaces.

I'm afraid I can't turn this into a reasonable function comment without
first amending the introduction comment to cover tree structure mapping.
Ugh.

Crazy thought: unboxing the implicit struct should make this interface
wart go away.  If we commit to do that later, we can "solve" our
documentation problem the same way as for visit_start_union(): FIXME
should not be needed.

>>> + *
>>> + * @obj must not be NULL, since this function is only called when
>>> + * visiting with qapi structs.
>> 
>> Really?  Why does qmp_input_start_implicit_struct() check for null then?
>
> Probably worth an independent cleanup.  The assertions added in this
> patch prove that that check is dead.

Please do that; such contradictions can be terribly confusing.

>>> +/**
>>> + * Prepare to visit a list tied to an object key @name.
>>> + * @name will be NULL if this is visited as part of another list.
>>> + * After calling this, the elements must be collected until
>>> + * visit_next_list() returns NULL, then visit_end_list() must be
>>> + * used to complete the visit.
>> 
>> I'm afraid this doesn't really tell me how to visit a list.  Perhaps:
>> 
>>     After visit_start_list() succeeds, the caller may visit its members
>>     one after the other, passing the value of visit_next_list() as @obj,
>>     until visit_next_list() returns NULL.  Finally, visit_end_list()
>>     needs to be called to clean up.
>> 
>> May want to add a pointer to example code in qapi-code-gen.txt.
>
> And it changes again in 34/37.

Reviewers are too myopic to see beyond the current patch, let alone 13
patches ahead :)

> We have two styles; our generated code, which is roughly:
>
> visit_start_list(&obj);
> while (visit_next_list()) {
>     visit_type_FOO(element);
> }
> visit_end_list()

Here, the list is real, i.e. an instance of a QAPI list type.

> and the manual style of our friend hw/ppc/spapr_drc.c:
>
> visit_start_list(NULL);
> while (some other way to decide if data available) {
>     visit_type_FOO(element);
> }
> visit_end_list()

Here, the list is virtual, but the list elements are real.

> That is, the use of visit_next_list() is optional depending on whether a
> QAPI list is in use, but the visit_type_FOO() per element is necessary.
>  We'll see if I can get the next wording attempt a bit easier to understand.
>> 
>>> + */
>>>  void visit_start_list(Visitor *v, const char *name, Error **errp);
>>> +/**
>
>>> + *
>>> + * Note that for some visitors (qapi-dealloc and qmp-output), when a
>>> + * qapi GenericList linked list is not being used (comparable to when
>>> + * a NULL obj is used for visit_start_struct()), it is acceptable to
>>> + * bypass the use of visit_next_list() and just directly call the
>>> + * appropriate visit_type_*() for each element in between the
>>> + * visit_start_list() and visit_end_list() calls.
>> 
>> I'm confused.  Can you point me to code bypassing visit_next_list()?
>
> See above; spapr_drc.c.

I see clearer now, thanks.

I think the documentation needs to explain the difference between
walking a real tree and walking a virtual tree, how to do either, and
what visitors need to implement to support either.

>>>  /**
>>     * Check if an optional member @name of an object needs visiting.
>>     * For input visitors, set *@present according to whether the
>>     * corresponding visit_type_*() needs calling; for other visitors,
>>     * leave *@present unchanged.  Return *@present for convenience.
>>    bool visit_optional(Visitor *v, const char *name, bool *present);
>> 
>> 
>> Suggest to clarify:
>> 
>>     Does optional struct member @name need visiting?
>>     @present points to the optional member's has_ flag.
>>     Input visitors set *@present according to their input.  Other
>>     visitors leave it unchanged.
>>     In either case, return *@present.
>
> Thanks. I originally added all the docs in one pass, but some of the
> docs snuck in via earlier patches during rebase churn, so reviewing it
> again here helps.
>
>>> +/**
>>> + * Visit a string value tied to @name in the current object visit.
>>> + * @name will be NULL if this is visited as part of a list.
>>> + * @obj must be non-NULL.
>> 
>> Same for the other visit_type_T(), but not specified there.
>> 
>>>                             Input visitors set *@obj to the parsed
>>> + * string (never NULL); while output visitors leave *@obj unchanged,
>>> + * except that a NULL *@obj will be treated the same as "".
>> 
>> Suggest:
>> 
>>     Input visitors set *@obj according to their input (never NULL).
>>     Other visitors leave it unchanged.  They commonly treat NULL like "".
>> 
>>> + *
>>> + * FIXME: Unfortunately not const-correct for output visitors.
>> 
>> Is that fixable?
>
> Not easily, and certainly not as part of this series.  The best I can
> think of is either two interfaces:
>
> char *visit_input_type_str(Visitor *v, const char *name, Error **errp);
> void visit_output_type_str(Visitor *v, const char *name, const char
> *value, Error **errp);
>
> used as:
>
> obj.str = visit_input_type_str(v, "foo", &err);
> visit_output_type_str(v, "foo", obj.str, &err);
>
> or to make the single interface have more parameters:
>
> void visit_type_str(Visitor *v, const char *name, const char *value_in,
> char **value_out, Error **errp);
>
> used as:
>
> visit_type_str(v, "foo", obj.str, &obj.str, &err);
>
> where input visits could pass NULL instead of value_in, and output
> visits could pass NULL instead of value_out.
>
> But either way, consistency would then argue that ALL visit_type_FOO()
> interfaces should either have two forms (one for input, one for output)
> or have more parameters (with input distinct from output), and it would
> allow const-correctness on output visits of more than just strings.  And
> which form would a dealloc visitor use?

What a headache...

I asked whether its fixable, because I don't like FIXMEs we can't or
don't intend to fix.

>>> +/**
>>> + * Mark the start of visiting the branches of a union. Return true if
>>> + * @data_present.
>> 
>> Not quite.  Actually returns true when the visitor doesn't provide this
>> callback, or else whatever its callback returns.  Only the dealloc
>> visitor provides it, and it returns @data_present.
>> 
>> You remove the function along with visit_end_union() in PATCH 32.  I'd
>> be okay with leaving them undocumented apart from the FIXME until then.
>> But if we add a contract now, it better be correct.
>
> I like the 'FIXME'-only approach.

Me too.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor Eric Blake
@ 2016-01-22 17:00   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-22 17:00 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Right now, qmp-output-visitor happens to produce a QNull result
> if nothing is actually visited between the creation of the visitor
> and the request for the resulting QObject.  A stronger protocol
> would require that a QMP output visit MUST visit something.  But
> to still be able to produce a JSON 'null' output, we need a new
> visitor function that states our intentions.

Overdue.  When we extended the json-parser to accept null, we neglected
to extend visitors accordingly.  We need to:

* Extend the visitor core (this patch).

* Implement it in at least the visitors that aren't restricted to a
  subset: dealloc (this patch), QMP input (next patch), QMP output
  (patch after next, together with other stuff).

* Update users, if any.

* If QAPI had a 'null' type, we'd also have to use it in the generated
  visitor functions.  It doesn't.  I think the 'any' type can hold a
  null, but that's it.  A 'null' type might be useful as an alternate
  member type.  We'll create one when we need it.

> This patch introduces the new visit_type_null() interface, and
> later patches will then wire it up into the qmp visitors.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to 'name' motion
> v7: new patch, based on discussion about spapr_drc.c
> ---
>  include/qapi/visitor-impl.h | 3 +++
>  include/qapi/visitor.h      | 8 ++++++++
>  qapi/qapi-dealloc-visitor.c | 5 +++++
>  qapi/qapi-visit-core.c      | 5 +++++
>  4 files changed, 21 insertions(+)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index aab46bc..8705136 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -75,6 +75,9 @@ struct Visitor
>       * visitors do not currently visit arbitrary types).  */
>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>                       Error **errp);
> +    /* Must be provided to visit explicit null values (right now, only the
> +     * dealloc visitor supports this).  */

Will need updating to match whatever convention we pick in the previous
patch for documenting "mandatory to visit X", and the restrictions on
visitor use resulting from some of them not implementing it.

> +    void (*type_null)(Visitor *v, const char *name, Error **errp);
>
>      /* May be NULL; most useful for input visitors. */
>      void (*optional)(Visitor *v, const char *name, bool *present);
[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit Eric Blake
@ 2016-01-22 17:12   ` Markus Armbruster
  2016-02-01 23:52     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-22 17:12 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Implement the new type_null() callback for the qmp input visitor.
> While we don't yet have a use for this in qapi (the generator
> will need some tweaks first), one usage is already envisioned:
> when changing blockdev parameters, it would be nice to have a
> difference between leaving a tuning parameter unchanged (omit
> that parameter from the struct) and to explicitly reset the
> parameter to its default without having to know what the default
> value is (specify the parameter with an explicit null value,
> which will require us to allow a qapi alternate that chooses
> between the normal value and an explicit null).
>
> At any rate, we can test this without the use of generated qapi
> by manually using visit_start_struct()/visit_end_struct().

Well, we test by calling visit_type_null() manually.  We choose to wrap
it in a visit_start_struct() ... visit_end_struct() pair, but that's
detail.  Actually, we do an unwrapped root visit first, and then a
struct-wrapped visit.

Suggest "by calling visit_type_null() manually."

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: new patch
> ---
>  include/qapi/visitor-impl.h    |  2 +-
>  qapi/qmp-input-visitor.c       | 14 ++++++++++++++
>  tests/test-qmp-input-visitor.c | 26 ++++++++++++++++++++++++++
>  3 files changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 8705136..95408a5 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -76,7 +76,7 @@ struct Visitor
>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>                       Error **errp);
>      /* Must be provided to visit explicit null values (right now, only the
> -     * dealloc visitor supports this).  */
> +     * dealloc and qmp-input visitors support this).  */

Will need updating.

>      void (*type_null)(Visitor *v, const char *name, Error **errp);
>
>      /* May be NULL; most useful for input visitors. */
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 597652c..ad23bec 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -315,6 +315,19 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
>      *obj = qobj;
>  }
>
> +static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
> +{
> +    QmpInputVisitor *qiv = to_qiv(v);
> +    QObject *qobj = qmp_input_get_object(qiv, name, true);
> +
> +    if (qobject_type(qobj) == QTYPE_QNULL) {
> +        return;
> +    }
> +
> +    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
> +               "null");

Recommend to put the error in the conditional:

    if (qobject_type(qobj) != QTYPE_QNULL) {
        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                   "null");
    }

> +}
> +
>  static void qmp_input_optional(Visitor *v, const char *name, bool *present)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> @@ -358,6 +371,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
>      v->visitor.type_str = qmp_input_type_str;
>      v->visitor.type_number = qmp_input_type_number;
>      v->visitor.type_any = qmp_input_type_any;
> +    v->visitor.type_null = qmp_input_type_null;
>      v->visitor.optional = qmp_input_optional;
>      v->visitor.get_next_type = qmp_input_get_next_type;
>
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index f6bd408..6489e4a 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -278,6 +278,30 @@ static void test_visitor_in_any(TestInputVisitorData *data,
>      qobject_decref(res);
>  }
>
> +static void test_visitor_in_null(TestInputVisitorData *data,
> +                                 const void *unused)
> +{
> +    Visitor *v;
> +    QObject *null;
> +
> +    v = visitor_input_test_init(data, "null");
> +    visit_type_null(v, NULL, &error_abort);
> +
> +    v = visitor_input_test_init(data, "{ 'a': null }");
> +    visit_start_struct(v, NULL, NULL, 0, &error_abort);
> +    visit_type_null(v, "a", &error_abort);
> +    visit_end_struct(v, &error_abort);
> +
> +    /* Check that qnull reference counting is sane:
> +     * 1 for global use, 1 for our qnull() use, and 1 still owned by 'v'
> +     * until it is torn down */
> +    null = qnull();
> +    g_assert(null->refcnt == 3);
> +    visitor_input_teardown(data, NULL);
> +    g_assert(null->refcnt == 2);
> +    qobject_decref(null);

For other kinds of QObject, we leave the testing of reference counting
to the check-qKIND.c, and don't bother with it when testing the
visitors.  Any particular reason to do null differently?

> +}
> +
>  static void test_visitor_in_union_flat(TestInputVisitorData *data,
>                                         const void *unused)
>  {
> @@ -792,6 +816,8 @@ int main(int argc, char **argv)
>                             &in_visitor_data, test_visitor_in_list);
>      input_visitor_test_add("/visitor/input/any",
>                             &in_visitor_data, test_visitor_in_any);
> +    input_visitor_test_add("/visitor/input/null",
> +                           &in_visitor_data, test_visitor_in_null);
>      input_visitor_test_add("/visitor/input/union-flat",
>                             &in_visitor_data, test_visitor_in_union_flat);
>      input_visitor_test_add("/visitor/input/alternate",

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules Eric Blake
@ 2016-01-22 19:11   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-22 19:11 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Add a new qmp_output_visitor_reset(), which must be called before
> reusing an exising QmpOutputVisitor on a new root object.  Tighten
> assertions to require that qmp_output_get_qobject() can only be
> called after pairing a visit_end_* for every visit_start_* (rather
> than allowing it to return a partially built object), that it must
> not be called unless at least one visit_type_* or visit_start/
> visit_end pair has occurred since creation/reset (the accidental
> return of NULL fixed by commit ab8bf1d7 would have been much
> easier to diagnose),

Makes sense.

>                      and that it may only be called once per visit.

Why?

Does it have a side effect?

> Meanwhile, nothing was using the return value of qmp_output_pop().

Well, pop returns the value popped, otherwise it's not a pop.

> Also, adding a parameter will let us diagnose any programming bugs
> due to mismatched push(struct)/pop(list) or push(list)/pop(struct).

Hmm.

> To keep the semantics of test_visitor_out_empty, we now have to
> explicitly request a top-level visit of a NULL object, by
> implementing the just-added visitor type_null() callback.

The fact that we implement type_null() in the QMP output visitor is
mentioned only in passing, and not clearly.

Could the previous patch implement it for both QMP input and output?

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: rebase to added patch, squash in more sanity checks, drop
> Marc-Andre's R-b
> v8: rename qmp_output_reset to qmp_output_visitor_reset
> v7: new patch, based on discussion about spapr_drc.c
> ---
>  include/qapi/qmp-output-visitor.h |  1 +
>  include/qapi/visitor-impl.h       |  4 ++--
>  qapi/qmp-output-visitor.c         | 50 +++++++++++++++++++++++----------------
>  tests/test-qmp-output-visitor.c   |  2 ++
>  4 files changed, 35 insertions(+), 22 deletions(-)
>
> diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
> index 2266770..5093f0d 100644
> --- a/include/qapi/qmp-output-visitor.h
> +++ b/include/qapi/qmp-output-visitor.h
> @@ -21,6 +21,7 @@ typedef struct QmpOutputVisitor QmpOutputVisitor;
>
>  QmpOutputVisitor *qmp_output_visitor_new(void);
>  void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
> +void qmp_output_visitor_reset(QmpOutputVisitor *v);
>
>  QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
>  Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 95408a5..913f1b0 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -75,8 +75,8 @@ struct Visitor
>       * visitors do not currently visit arbitrary types).  */
>      void (*type_any)(Visitor *v, const char *name, QObject **obj,
>                       Error **errp);
> -    /* Must be provided to visit explicit null values (right now, only the
> -     * dealloc and qmp-input visitors support this).  */
> +    /* Must be provided to visit explicit null values (the opts and string
> +     * visitors do not currently visit an explicit null).  */

Will need updating.

>      void (*type_null)(Visitor *v, const char *name, Error **errp);
>
>      /* May be NULL; most useful for input visitors. */
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index df22999..2eb200d 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -1,6 +1,7 @@
>  /*
>   * Core Definitions for QAPI/QMP Command Registry
>   *
> + * Copyright (C) 2015-2016 Red Hat, Inc.
>   * Copyright IBM, Corp. 2011
>   *
>   * Authors:
> @@ -56,17 +57,15 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> -/* Grab and remove the most recent QObject from the stack */
> -static QObject *qmp_output_pop(QmpOutputVisitor *qov)
> +/* Remove the most recent QObject with given type from the stack */
> +static void qmp_output_pop(QmpOutputVisitor *qov, QType type)
>  {
>      QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> -    QObject *value;
>
>      assert(e);
>      QTAILQ_REMOVE(&qov->stack, e, node);
> -    value = e->value;
> +    assert(qobject_type(e->value) == type);
>      g_free(e);
> -    return value;
>  }
>
>
>  /* Grab the most recent QObject from the stack, if any */
> @@ -88,9 +87,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>      cur = qmp_output_last(qov);
>
>      if (!cur) {
> -        /* FIXME we should require the user to reset the visitor, rather
> -         * than throwing away the previous root */
> -        qobject_decref(qov->root);
> +        /* Don't allow reuse of visitor on more than one root */
> +        assert(!qov->root);
>          qov->root = value;
>      } else {
>          switch (qobject_type(cur)) {
> @@ -99,6 +97,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>              qdict_put_obj(qobject_to_qdict(cur), name, value);
>              break;
>          case QTYPE_QLIST:
> +            assert(!name);

Okay if we put "name must be null when visiting list elements" in the
contract.

However, I believe decent error messages will require a non-null name.

I'd specify "may be null" instead of "must be null", and drop the
assertion, because one, it's what the code actually needs, and two,
it'll be slightly less churn when we fix the error messages.

>              qlist_append_obj(qobject_to_qlist(cur), value);
>              break;
>          default:
> @@ -120,7 +119,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
>  static void qmp_output_end_struct(Visitor *v, Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
> -    qmp_output_pop(qov);
> +    qmp_output_pop(qov, QTYPE_QDICT);

Leave qmp_output_pop() unchanged, and do

    value = qmp_output_pop(qov);
    assert(qobject_type(value) == QTYPE_QLIST);

Likewise for the second caller.

Just as simple, and the thing called pop actually pops.

>  }
>
>  static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
> @@ -151,7 +150,7 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
>  static void qmp_output_end_list(Visitor *v)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
> -    qmp_output_pop(qov);
> +    qmp_output_pop(qov, QTYPE_QLIST);
>  }
>
>  static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj,
> @@ -202,18 +201,22 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
>      qmp_output_add_obj(qov, name, *obj);
>  }
>
> +static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
> +{
> +    QmpOutputVisitor *qov = to_qov(v);
> +    qmp_output_add_obj(qov, name, qnull());
> +}
> +
>  /* Finish building, and return the root object. Will not be NULL. */
>  QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
>  {
> -    /* FIXME: we should require that a visit occurred, and that it is
> -     * complete (no starts without a matching end) */
> -    QObject *obj = qov->root;
> -    if (obj) {
> -        qobject_incref(obj);
> -    } else {
> -        obj = qnull();
> -    }
> -    return obj;
> +    QObject *root;
> +
> +    assert(qov->root);              /* A visit must have occurred...  */
> +    assert(!qmp_output_last(qov));  /* ...with each start paired with end.  */

I figure QTAILQ_EMPTY(&qov->stack) would be more obvious.

Apropos QTAILQ: where I learned my trade, you got laughed out of the lab
for implementing such a stack with a dynamically allocated linked list.

> +    root = qov->root;
> +    qov->root = NULL;

This line arbitrarily restricts us to a single get.  Replace it by
qobject_incref(root), and qobject_decref(qmp_output_get_qobject(qov)) is
idempotent.  Idempotent is lovely.

> +    return root;
>  }
>
>  Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
> @@ -221,7 +224,7 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
>      return &v->visitor;
>  }
>
> -void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> +void qmp_output_visitor_reset(QmpOutputVisitor *v)
>  {
>      QStackEntry *e, *tmp;
>
> @@ -231,6 +234,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
>      }
>
>      qobject_decref(v->root);
> +    v->root = NULL;
> +}
> +
> +void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> +{
> +    qmp_output_visitor_reset(v);
>      g_free(v);
>  }
>
> @@ -252,6 +261,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
>      v->visitor.type_str = qmp_output_type_str;
>      v->visitor.type_number = qmp_output_type_number;
>      v->visitor.type_any = qmp_output_type_any;
> +    v->visitor.type_null = qmp_output_type_null;
>
>      QTAILQ_INIT(&v->stack);
>
> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index 26dc752..74d0ac4 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -260,6 +260,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
>          visit_type_UserDefOne(data->ov, "unused", &pu, &err);
>          g_assert(err);
>          error_free(err);
> +        qmp_output_visitor_reset(data->qov);
>      }
>  }
>

That's the only spot that uses visitors for multiple roots?  I expected
worse...

> @@ -459,6 +460,7 @@ static void test_visitor_out_empty(TestOutputVisitorData *data,
>  {
>      QObject *arg;
>
> +    visit_type_null(data->ov, NULL, &error_abort);
>      arg = qmp_output_get_qobject(data->qov);
>      g_assert(qobject_type(arg) == QTYPE_QNULL);
>      /* Check that qnull reference counting is sane */

This isn't testing "empty" anymore.  Suggest to s/empty/null/.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
@ 2016-01-22 19:15   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-22 19:15 UTC (permalink / raw)
  To: Eric Blake
  Cc: marcandre.lureau, David Gibson, open list:sPAPR, qemu-devel,
	Alexander Graf

Eric Blake <eblake@redhat.com> writes:

> Now that the QMP output visitor supports an explicit null
> output, we should utilize it to make it easier to diagnose
> the difference between a missing fdt vs. a present-but-empty
> one.

Please spell out that we change the value from {} to null.

>
> (Note that this reverts the behavior of commit ab8bf1d, taking
> us back to the behavior of commit 6c2f9a1 [which in turn
> stemmed from a crash fix in 1d10b44]; but that this time,
> the change is intentional and not an accidental side-effect.)
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Acked-by: David Gibson <david@gibson.dropbear.id.au>
>
> ---
> v9: improved commit message
> v8: rebase to 'name' motion
> v7: new patch, based on discussion about spapr_drc.c
> ---
>  hw/ppc/spapr_drc.c | 6 +-----
>  1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
> index ffc2cd9..831ce23 100644
> --- a/hw/ppc/spapr_drc.c
> +++ b/hw/ppc/spapr_drc.c
> @@ -259,11 +259,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
>      void *fdt;
>
>      if (!drc->fdt) {
> -        visit_start_struct(v, name, NULL, 0, &err);
> -        if (!err) {
> -            visit_end_struct(v, &err);
> -        }
> -        error_propagate(errp, err);
> +        visit_type_null(v, NULL, errp);
>          return;
>      }

Easier to read, always welcome :)

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
@ 2016-01-22 19:24   ` Markus Armbruster
  2016-01-22 19:37     ` Eric Blake
  2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
  1 sibling, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-22 19:24 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> When reporting that an unvisited member remains at the end of an
> input visit for a struct, we were using g_hash_table_find()
> coupled with a callback function that always returns true, to
> locate an arbitrary member of the hash table.  But if all we
> need is an arbitrary entry, we can get that from a single-use
> iterator, without needing a tautological callback function.

Good idea.

> Suggested-by: Markus Armbruster <armbru@redhat.com>

Whoops, it's even mine!  I forgot... %-)

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to earlier changes
> v7: retitle, rebase to earlier context changes
> v6: new patch, based on comments on RFC against v5 7/46
> ---
>  qapi/opts-visitor.c      | 12 +++---------
>  qapi/qmp-input-visitor.c | 14 +++++---------
>  2 files changed, 8 insertions(+), 18 deletions(-)
>
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 62ffdd4..df312e6 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -156,17 +156,11 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>  }
>
>
> -static gboolean
> -ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
> -{
> -    return TRUE;
> -}
> -
> -
>  static void
>  opts_end_struct(Visitor *v, Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
> +    GHashTableIter iter;
>      GQueue *any;
>
>      if (--ov->depth > 0) {
> @@ -174,8 +168,8 @@ opts_end_struct(Visitor *v, Error **errp)
>      }
>
>      /* we should have processed all (distinct) QemuOpt instances */
> -    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
> -    if (any) {
> +    g_hash_table_iter_init(&iter, ov->unprocessed_opts);
> +    if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {

Is this cast kosher?

>          const QemuOpt *first;
>
>          first = g_queue_peek_head(any);
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index ad23bec..91ef945 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -88,12 +88,6 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
>      qiv->nb_stack++;
>  }
>
> -/** Only for qmp_input_pop. */
> -static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey)
> -{
> -    *(const char **)user_pkey = (const char *)key;
> -    return TRUE;
> -}
>
>  static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
>  {
> @@ -102,9 +96,11 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
>      if (qiv->strict) {
>          GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
>          if (top_ht) {
> -            if (g_hash_table_size(top_ht)) {
> -                const char *key;
> -                g_hash_table_find(top_ht, always_true, &key);
> +            GHashTableIter iter;
> +            const char *key;
> +
> +            g_hash_table_iter_init(&iter, top_ht);
> +            if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {

Is this cast kosher?

>                  error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
>              }
>              g_hash_table_unref(top_ht);

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors
  2016-01-22 19:24   ` Markus Armbruster
@ 2016-01-22 19:37     ` Eric Blake
  2016-01-25  9:27       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-22 19:37 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: marcandre.lureau, Laszlo Ersek, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 2594 bytes --]

On 01/22/2016 12:24 PM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> When reporting that an unvisited member remains at the end of an
>> input visit for a struct, we were using g_hash_table_find()
>> coupled with a callback function that always returns true, to
>> locate an arbitrary member of the hash table.  But if all we
>> need is an arbitrary entry, we can get that from a single-use
>> iterator, without needing a tautological callback function.
> 
> Good idea.
> 
>> Suggested-by: Markus Armbruster <armbru@redhat.com>
> 
> Whoops, it's even mine!  I forgot... %-)
> 
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>
>> ---

>>      GQueue *any;
>>
>>      if (--ov->depth > 0) {
>> @@ -174,8 +168,8 @@ opts_end_struct(Visitor *v, Error **errp)
>>      }
>>
>>      /* we should have processed all (distinct) QemuOpt instances */
>> -    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
>> -    if (any) {
>> +    g_hash_table_iter_init(&iter, ov->unprocessed_opts);
>> +    if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
> 
> Is this cast kosher?

You may have a point that it violates some corner of C99, but I'm not
the first such user:

hw/i386/intel_iommu.c:    while (g_hash_table_iter_next (&bus_it, NULL,
(void**)&vtd_bus)) {
qom/object.c:    while (g_hash_table_iter_next(&iter, NULL, (gpointer
*)&prop)) {

Conceptually, it seems fine - void* can be assigned to any pointer, and
'any' qualifies as such a pointer.  We are then taking the address of
that (or void**) to pass by reference.

I suppose a stricter version would be:

void *wrap;
GQueue *any;
if (g_hash_table_iter_next(&iter, NULL, &wrap)) {
    any = wrap;
...

but is it worth the bother?  Put another way, will a compiler ever do
the wrong thing to us because C99 might have some corner case?  Laszlo,
what's your take?

>>          if (top_ht) {
>> -            if (g_hash_table_size(top_ht)) {
>> -                const char *key;
>> -                g_hash_table_find(top_ht, always_true, &key);
>> +            GHashTableIter iter;
>> +            const char *key;
>> +
>> +            g_hash_table_iter_init(&iter, top_ht);
>> +            if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
> 
> Is this cast kosher?

Here, in addition to the above argument, we also needed to cast away const.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors
  2016-01-22 19:37     ` Eric Blake
@ 2016-01-25  9:27       ` Markus Armbruster
  2016-01-25 10:43         ` Laszlo Ersek
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-25  9:27 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, Laszlo Ersek, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/22/2016 12:24 PM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> When reporting that an unvisited member remains at the end of an
>>> input visit for a struct, we were using g_hash_table_find()
>>> coupled with a callback function that always returns true, to
>>> locate an arbitrary member of the hash table.  But if all we
>>> need is an arbitrary entry, we can get that from a single-use
>>> iterator, without needing a tautological callback function.
>> 
>> Good idea.
>> 
>>> Suggested-by: Markus Armbruster <armbru@redhat.com>
>> 
>> Whoops, it's even mine!  I forgot... %-)
>> 
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>>
>>> ---
>
>>>      GQueue *any;
>>>
>>>      if (--ov->depth > 0) {
>>> @@ -174,8 +168,8 @@ opts_end_struct(Visitor *v, Error **errp)
>>>      }
>>>
>>>      /* we should have processed all (distinct) QemuOpt instances */
>>> -    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
>>> -    if (any) {
>>> +    g_hash_table_iter_init(&iter, ov->unprocessed_opts);
>>> +    if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
>> 
>> Is this cast kosher?
>
> You may have a point that it violates some corner of C99, but I'm not
> the first such user:
>
> hw/i386/intel_iommu.c:    while (g_hash_table_iter_next (&bus_it, NULL,
> (void**)&vtd_bus)) {
> qom/object.c:    while (g_hash_table_iter_next(&iter, NULL, (gpointer
> *)&prop)) {
>
> Conceptually, it seems fine - void* can be assigned to any pointer, and
> 'any' qualifies as such a pointer.  We are then taking the address of
> that (or void**) to pass by reference.

Yes, any pointer can be converted to and from void *.  Doesn't mean the
conversion results in the same bits.  It does on machines with a single,
uniform pointer format, i.e. any sane machine.

(void **)iter converts iter, not *iter.  *iter gets reinterpreted on
dereference.

You're right in that this kind of type punning already occurs in QEMU.
Also in other programs.  We can hope it's common enough to deter
optimizers from screwing it up even on sane machines.

> I suppose a stricter version would be:
>
> void *wrap;
> GQueue *any;
> if (g_hash_table_iter_next(&iter, NULL, &wrap)) {
>     any = wrap;
> ...
>
> but is it worth the bother?  Put another way, will a compiler ever do
> the wrong thing to us because C99 might have some corner case?  Laszlo,
> what's your take?

Perhaps Laszlo can confirm or refute my reading of the standard.

>>>          if (top_ht) {
>>> -            if (g_hash_table_size(top_ht)) {
>>> -                const char *key;
>>> -                g_hash_table_find(top_ht, always_true, &key);
>>> +            GHashTableIter iter;
>>> +            const char *key;
>>> +
>>> +            g_hash_table_iter_init(&iter, top_ht);
>>> +            if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
>> 
>> Is this cast kosher?
>
> Here, in addition to the above argument, we also needed to cast away const.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors
  2016-01-25  9:27       ` Markus Armbruster
@ 2016-01-25 10:43         ` Laszlo Ersek
  0 siblings, 0 replies; 128+ messages in thread
From: Laszlo Ersek @ 2016-01-25 10:43 UTC (permalink / raw)
  To: Markus Armbruster, Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

On 01/25/16 10:27, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> On 01/22/2016 12:24 PM, Markus Armbruster wrote:
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> When reporting that an unvisited member remains at the end of an
>>>> input visit for a struct, we were using g_hash_table_find()
>>>> coupled with a callback function that always returns true, to
>>>> locate an arbitrary member of the hash table.  But if all we
>>>> need is an arbitrary entry, we can get that from a single-use
>>>> iterator, without needing a tautological callback function.
>>>
>>> Good idea.
>>>
>>>> Suggested-by: Markus Armbruster <armbru@redhat.com>
>>>
>>> Whoops, it's even mine!  I forgot... %-)

Side remark:

https://developer.gnome.org/glib/stable/glib-Hash-Tables.html

g_hash_table_find (): Since: 2.4
g_hash_table_iter_next (): Since: 2.16

I can't prove that when I wrote this code, g_hash_table_iter_next()
wasn't available, but I may easily have googled & looked at GLib
*documentation* that lacked g_hash_table_iter_next(). :)

>>>
>>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>>>
>>>> ---
>>
>>>>      GQueue *any;
>>>>
>>>>      if (--ov->depth > 0) {
>>>> @@ -174,8 +168,8 @@ opts_end_struct(Visitor *v, Error **errp)
>>>>      }
>>>>
>>>>      /* we should have processed all (distinct) QemuOpt instances */
>>>> -    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL);
>>>> -    if (any) {
>>>> +    g_hash_table_iter_init(&iter, ov->unprocessed_opts);
>>>> +    if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
>>>
>>> Is this cast kosher?
>>
>> You may have a point that it violates some corner of C99, but I'm not
>> the first such user:
>>
>> hw/i386/intel_iommu.c:    while (g_hash_table_iter_next (&bus_it, NULL,
>> (void**)&vtd_bus)) {
>> qom/object.c:    while (g_hash_table_iter_next(&iter, NULL, (gpointer
>> *)&prop)) {
>>
>> Conceptually, it seems fine - void* can be assigned to any pointer, and
>> 'any' qualifies as such a pointer.  We are then taking the address of
>> that (or void**) to pass by reference.
> 
> Yes, any pointer can be converted to and from void *.  Doesn't mean the
> conversion results in the same bits.  It does on machines with a single,
> uniform pointer format, i.e. any sane machine.
> 
> (void **)iter converts iter, not *iter.  *iter gets reinterpreted on
> dereference.
> 
> You're right in that this kind of type punning already occurs in QEMU.
> Also in other programs.  We can hope it's common enough to deter
> optimizers from screwing it up even on sane machines.
> 
>> I suppose a stricter version would be:
>>
>> void *wrap;
>> GQueue *any;
>> if (g_hash_table_iter_next(&iter, NULL, &wrap)) {
>>     any = wrap;
>> ...
>>
>> but is it worth the bother?  Put another way, will a compiler ever do
>> the wrong thing to us because C99 might have some corner case?  Laszlo,
>> what's your take?
> 
> Perhaps Laszlo can confirm or refute my reading of the standard.

The concern is justified; the expression "(void **)&any" does not
convert a pointer-to-void to pointer-to-GQueue, it causes the bit
pattern to be reinterpreted (it is stored as pointer-to-void and
reinterpreted as pointer-to-GQueue).

6.2.5 Types p27 says "A pointer to void shall have the same
representation and alignment requirements as a
pointer to a character type. [...] All pointers to structure types shall
have the same representation and alignment requirements as each other."

(I'll assume that GQueue is a struct.) But, they need not match each
other. At worst, "any" might not even have enough storage for a
pointer-to-void.

I'll mention that edk2 is chock-full of the above style cast. I think
even the UEFI spec is, in code examples. But, edk2 builds with
-fno-strict-aliasing & friends.

... I think it doesn't matter in practice, but if we're trying to
prevent compilers from outsmarting us, I'd feel better about "any = wrap".

Thanks
Laszlo


> 
>>>>          if (top_ht) {
>>>> -            if (g_hash_table_size(top_ht)) {
>>>> -                const char *key;
>>>> -                g_hash_table_find(top_ht, always_true, &key);
>>>> +            GHashTableIter iter;
>>>> +            const char *key;
>>>> +
>>>> +            g_hash_table_iter_init(&iter, top_ht);
>>>> +            if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
>>>
>>> Is this cast kosher?
>>
>> Here, in addition to the above argument, we also needed to cast away const.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper Eric Blake
@ 2016-01-25 14:15   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-25 14:15 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> And use it in qapi-types and qapi-event.  Down the road, we may
> want to lift our artificial restriction of no variants at the
> top level of an event, at which point, inlining our check for
> whether members is empty will no longer be sufficient.  More
> immediately, the new .is_empty() helper will help fix a bug in
> qapi-visit in the next patch, where the generator did not handle
> an explicit but empty type in the same was as a missing type.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: improve commit message
> v8: no change
> v7: rebase to context change
> v6: new patch
> ---
>  scripts/qapi-event.py | 6 +++---
>  scripts/qapi-types.py | 2 +-
>  scripts/qapi.py       | 3 +++
>  3 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index a1dca29..56c93a8 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -39,7 +39,7 @@ def gen_event_send(name, arg_type):
>  ''',
>                  proto=gen_event_send_proto(name, arg_type))
>
> -    if arg_type and arg_type.members:
> +    if arg_type and not arg_type.is_empty():

With .is_empty() expanded and negations folded:

       if arg_type and (arg_type.members or arg_type.variants):

Effectively the same, because .variants is None here.  Asserted in
QAPISchemaEvent.check().

>          ret += mcgen('''
>      QmpOutputVisitor *qov;
>      Visitor *v;
> @@ -58,7 +58,7 @@ def gen_event_send(name, arg_type):
>  ''',
>                   name=name)
>
> -    if arg_type and arg_type.members:
> +    if arg_type and not arg_type.is_empty():

Likewise.

However, we still use arg_type.members:

>          ret += mcgen('''
>      qov = qmp_output_visitor_new();
>      v = qmp_output_get_visitor(qov);

       visit_start_struct(v, "%(name)s", NULL, 0, &err);
   ''',
                        name=name)
           ret += gen_err_check()
           ret += gen_visit_fields(arg_type.members, need_cast=True,
                                   label='out_obj')
           ret += mcgen('''
   out_obj:
       visit_end_struct(v, &err);
       if (err) {
           goto out;
       }

Your patch makes liberates the guard from the implementation, but not
the actual use.  That's okay.  Should we comment or assert that variants
aren't implemented here, even though we already assert in
QAPISchemaEvent.check()?

> @@ -88,7 +88,7 @@ out_obj:
>  ''',
>                   c_enum=c_enum_const(event_enum_name, name))
>
> -    if arg_type and arg_type.members:
> +    if arg_type and not arg_type.is_empty():
>          ret += mcgen('''
>  out:
>      qmp_output_visitor_cleanup(qov);
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index d3f631a..c70fae1 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -76,7 +76,7 @@ struct %(c_name)s {
>      # potential issues with attempting to malloc space for zero-length
>      # structs in C, and also incompatibility with C++ (where an empty
>      # struct is size 1).
> -    if not (base and base.members) and not members and not variants:
> +    if (not base or base.is_empty()) and not members and not variants:
>          ret += mcgen('''
>      char qapi_dummy_field_for_empty_struct;
>  ''')

This is similar: effectively the same because base.variants is None.

I'm pretty sure we check base is a plain struct (no variants) somewhere.
I think we should also assert not base.variants in
QAPISchemaObjectType.check(), for completeness and clarity.

> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 3b4c62e..a13c110 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -973,6 +973,9 @@ class QAPISchemaObjectType(QAPISchemaType):
>          # See QAPISchema._make_implicit_object_type()
>          return self.name[0] == ':'
>
> +    def is_empty(self):
> +        return not self.members and not self.variants
> +

Works only after .check().  Before .check(), self.members is None.  Your
uses are all safely after .check().

Suggest to assert self.members is not None.

>      def c_name(self):
>          assert not self.is_implicit()
>          return QAPISchemaType.c_name(self)

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type Eric Blake
@ 2016-01-25 15:03   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-25 15:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The generator special-cased
>  { 'command':'foo', 'data': {} }
> to avoid emitting a visitor variable, but failed to see that
>  { 'struct':'NamedEmptyType, 'data': {} }
>  { 'command':'foo', 'data':'NamedEmptyType' }
> needs the same treatment.  Without a fix to the generator, the
> change to qapi-schema-test.json fails to compile with:
>
> tests/test-qmp-marshal.c: In function ‘qmp_marshal_user_def_cmd0’:
> tests/test-qmp-marshal.c:264:14: error: variable ‘v’ set but not used [-Werror=unused-but-set-variable]
>      Visitor *v;
>               ^
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: no change
> v7: no change
> v6: new patch
> ---
>  scripts/qapi-commands.py                | 6 +++---
>  tests/qapi-schema/qapi-schema-test.json | 2 ++
>  tests/qapi-schema/qapi-schema-test.out  | 2 ++
>  tests/test-qmp-commands.c               | 5 +++++
>  4 files changed, 12 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index 91c5a4e..00ee565 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -65,7 +65,7 @@ def gen_marshal_vars(arg_type, ret_type):
>  ''',
>                       c_type=ret_type.c_type())
>
> -    if arg_type:
> +    if arg_type and not arg_type.is_empty():

This guards generating the variables for getting arguments, first a few
common ones, then one or two per argument.

The patch changes it to effectively

       if arg_type and (arg_type.members or arg_type.variants)

.variants is None (asserted in QAPISchemaCommand.check()).  Thus, the
patch effectively just adds and arg_type.members.

Impact: when arg_type has no members, we still generate the common
variables before the patch.  The patch suppresses them.  Whether that
makes sense depends on the next hunk, which generates the code using
these variables.

>          ret += mcgen('''
>      QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
>      QapiDeallocVisitor *qdv;
> @@ -97,7 +97,7 @@ def gen_marshal_vars(arg_type, ret_type):
>  def gen_marshal_input_visit(arg_type, dealloc=False):
>      ret = ''
>
> -    if not arg_type:
> +    if not arg_type or arg_type.is_empty():
>          return ret

This guards generating the code for getting arguments: common code, and
per-argument code.

Impact: when arg_type has no members, we still generate the common code
before the patch.  The patch suppresses it.  Is that okay?  We'll see
below.

>
>      if dealloc:
> @@ -177,7 +177,7 @@ def gen_marshal(name, arg_type, ret_type):
>
>      # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
>      # for each arg_type member, and by gen_call() for ret_type
> -    if (arg_type and arg_type.members) or ret_type:
> +    if (arg_type and not arg_type.is_empty()) or ret_type:

This guards generating label out.  or's left operand is about the
arguments.  Unlike the guards above, it checks .members even before the
patch.

>          ret += mcgen('''
>
>  out:
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 4b89527..a0fdb88 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -18,6 +18,8 @@
>  { 'struct': 'Empty1', 'data': { } }
>  { 'struct': 'Empty2', 'base': 'Empty1', 'data': { } }
>
> +{ 'command': 'user_def_cmd0', 'data': 'Empty2', 'returns': 'Empty2' }
> +
>  # for testing override of default naming heuristic
>  { 'enum': 'QEnumTwo',
>    'prefix': 'QENUM_TWO',
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 2c546b7..d8f9108 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -198,6 +198,8 @@ command guest-sync :obj-guest-sync-arg -> any
>     gen=True success_response=True
>  command user_def_cmd None -> None
>     gen=True success_response=True
> +command user_def_cmd0 Empty2 -> Empty2
> +   gen=True success_response=True
>  command user_def_cmd1 :obj-user_def_cmd1-arg -> None
>     gen=True success_response=True
>  command user_def_cmd2 :obj-user_def_cmd2-arg -> UserDefTwo
> diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
> index 4d267b6..bc8085d 100644
> --- a/tests/test-qmp-commands.c
> +++ b/tests/test-qmp-commands.c
> @@ -12,6 +12,11 @@ void qmp_user_def_cmd(Error **errp)
>  {
>  }
>
> +Empty2 *qmp_user_def_cmd0(Error **errp)
> +{
> +    return g_new0(Empty2, 1);
> +}
> +
>  void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
>  {
>  }

Patch's effect on the test case:

--- bld-x86/tests/test-qmp-marshal.c	2016-01-25 15:54:34.106265263 +0100
+++ bld-x86/tests/new-test-qmp-marshal.c	2016-01-25 15:54:08.312574713 +0100
@@ -254,11 +254,8 @@
 {
     Error *err = NULL;
     Empty2 *retval;
-    QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
-    QapiDeallocVisitor *qdv;
-    Visitor *v;
 
-    v = qmp_input_get_visitor(qiv);
+    (void)args;
 
     retval = qmp_user_def_cmd0(&err);
     if (err) {
@@ -269,10 +266,6 @@
 
 out:
     error_propagate(errp, err);
-    qmp_input_visitor_cleanup(qiv);
-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
-    qapi_dealloc_visitor_cleanup(qdv);
 }
 
 static void qmp_marshal_user_def_cmd1(QDict *args, QObject **ret, Error **errp)

We drop a pair of "create a visitor, destroy it without having done
anything with it".  Okay.

Commit message might mislead readers into assuming the patch merely
drops unused variables.  It actually drops creating useless visitors.
Easy enough to clarify:

    The generator special-cased

        { 'command':'foo', 'data': {} }
    to avoid emitting a visitor variable, but failed to see that

        { 'struct':'NamedEmptyType, 'data': {} }
        { 'command':'foo', 'data':'NamedEmptyType' }

    needs the same treatment.  There, the generator happily generates a
    visitor to get no arguments, and a visitor to destroy no arguments.
    The compiler isn't happy with that, as demonstrated by the updated
    qapi-schema-test.json:

        tests/test-qmp-marshal.c: In function ‘qmp_marshal_user_def_cmd0’:
        tests/test-qmp-marshal.c:264:14: error: variable ‘v’ set but not used [-Werror=unused-but-set-variable]
             Visitor *v;
                      ^

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
@ 2016-01-25 17:04   ` Markus Armbruster
  2016-02-17  4:57     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-25 17:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> For empty structs, such as the 'Abort' helper type used as part
> of the 'transaction' command, we were emitting a no-op
> visit_type_FOO_fields().  Optimize things to instead omit calls
> for empty structs.  Generated code changes resemble:
>
> |-static void visit_type_Abort_fields(Visitor *v, Abort **obj, Error **errp)
> |-{
> |-    Error *err = NULL;
> |-
> |-    error_propagate(errp, err);
> |-}
> |-
> | void visit_type_Abort(Visitor *v, const char *name, Abort **obj, Error **errp)
> | {
> |     Error *err = NULL;
> |@@ -112,7 +105,6 @@ void visit_type_Abort(Visitor *v, Abort
> |     if (!*obj) {
> |         goto out_obj;
> |     }
> |-    visit_type_Abort_fields(v, obj, &err);
> | out_obj:
> |     error_propagate(errp, err);
>
> Another reason for doing this optimization is that it gets us
> closer to merging the code for visiting structs and unions:
> since flat unions have no local members, they do not need to
> have a visit_type_UNION_fields() emitted, even when they start
> sharing the code used to visit structs.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to earlier changes
> v7: rebase to earlier changes
> v6: new patch
> ---
>  scripts/qapi-visit.py | 41 +++++++++++++++++++++++------------------
>  1 file changed, 23 insertions(+), 18 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 573bb81..6537a20 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -35,22 +35,22 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **

Two clean ways to skin this cat (at least).  One emphasizes that
gen_visit_fields_decl() & friends do nothing when the type is empty.
The other emphasizes that they generate at most once.  Matter of taste.

The two ways differ in how they treat the seen sets:

   # visit_type_FOO_implicit() is emitted as needed; track if it has already
   # been output.
   implicit_structs_seen = set()

   # visit_type_FOO_fields() is always emitted; track if a forward declaration
   # or implementation has already been output.
   struct_fields_seen = set()

To emphasize do nothing when empty, test empty first.  iEmpty types are
never added to these seen sets.

To emphasize generate at most once, test the seen set first.  Empty
types are added normally, even though nothing is output for them.

>
>
>  def gen_visit_fields_decl(typ):
> -    ret = ''
> -    if typ.name not in struct_fields_seen:
> -        ret += mcgen('''
> +    if typ.is_empty() or typ.name in struct_fields_seen:
> +        return ''
> +
> +    struct_fields_seen.add(typ.name)
> +    return mcgen('''
>
>  static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
>  ''',
> -                     c_type=typ.c_name())
> -        struct_fields_seen.add(typ.name)
> -    return ret
> +                 c_type=typ.c_name())

Emphasize do nothing when empty:

   def gen_visit_fields_decl(typ):
       if typ.is_empty():
           return ''
       if typ.name in struct_fields_seen:
           return ''
       struct_fields_seen.add(typ.name)
       return mcgen('''

   static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
   ''',
                        c_type=typ.c_name())

Same as yours, except yours merges the conditionals.  I left them
unmerged to make the

    if X not in S:
        return ''
    S.add(X)

pattern stand out.  Matter of taste.

Emphasize generate at most once:

   def gen_visit_fields_decl(typ):
       if typ.name in struct_fields_seen:
           return ''
       struct_fields_seen.add(typ.name)
       if typ.is_empty():
           return ''
       return mcgen('''

   static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
   ''',
                        c_type=typ.c_name())

>
>
>  def gen_visit_implicit_struct(typ):
> -    if typ in implicit_structs_seen:
> +    if typ.is_empty() or typ in implicit_structs_seen:
>          return ''
> +
>      implicit_structs_seen.add(typ)
> -
>      ret = gen_visit_fields_decl(typ)
>

Likewise.

>      ret += mcgen('''
> @@ -74,7 +74,10 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
>  def gen_visit_struct_fields(name, base, members):
>      ret = ''
>
> -    if base:
> +    if (not base or base.is_empty()) and not members:
> +        return ret

The condition predicts whether the rest of the function won't generate
anything useful.  Such predictions can be brittle, but this one seems
still simple enough.

> +
> +    if base and not base.is_empty():
>          ret += gen_visit_fields_decl(base)

"if base" suffices, because gen_visit_fields_decl(base) returns '' when
base.is_empty().  I guess you test it anyway, for symmetry with the next
hunk.  Okay.

>
>      struct_fields_seen.add(name)
> @@ -87,7 +90,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>  ''',
>                   c_name=c_name(name))
>
> -    if base:
> +    if base and not base.is_empty():
>          ret += mcgen('''
>      visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
>  ''',
> @@ -96,13 +99,9 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>
>      ret += gen_visit_fields(members, prefix='(*obj)->')
>
> -    # 'goto out' produced for base, and by gen_visit_fields() for each member
> -    if base or members:
> -        ret += mcgen('''
> +    ret += mcgen('''
>
>  out:
> -''')
> -    ret += mcgen('''
>      error_propagate(errp, err);
>  }
>  ''')

The optimization earns a small part of its keep here: we don't need a
conditional for the out label anymore.

> @@ -129,7 +128,14 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>      if (!*obj) {
>          goto out_obj;
>      }
> +''',
> +                 name=name, c_name=c_name(name))
> +    if (base and not base.is_empty()) or members:

Duplicates gen_visit_struct_fields()' guard.  Could be avoided by
testing whether gen_visit_struct_fields() returned anything.  Either way
is kind of ugly, though.  Pick the poison you hate less.

> +        ret += mcgen('''
>      visit_type_%(c_name)s_fields(v, obj, &err);
> +''',
> +                     c_name=c_name(name))
> +    ret += mcgen('''
>  out_obj:
>      error_propagate(errp, err);
>      err = NULL;
> @@ -137,8 +143,7 @@ out_obj:
>  out:
>      error_propagate(errp, err);
>  }
> -''',
> -                 c_name=c_name(name))
> +''')
>
>      return ret
>
> @@ -300,7 +305,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>  ''',
>                           c_type=simple_union_type.c_name(),
>                           c_name=c_name(var.name))
> -        else:
> +        elif not var.type.is_empty():
>              ret += mcgen('''
>          visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
>  ''',

Similar, for gen_visit_implicit_struct().  The condition is more obvious
here.

I'm not sure the optimization is worthwhile by itself.  Empty structs
are rare.  I'm reserving judgement until I see the struct/union
unification.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty Eric Blake
@ 2016-01-25 19:04   ` Markus Armbruster
  2016-01-26 16:29     ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-25 19:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Now that we elide unnecessary visits of empty types, we can
> start using the special ':empty' type in more places.  By using
> the empty type as the base class of every explicit struct or
> union, and as the default data for any command or event, we can
> simplify later logic in qapi-{visit,commands,event} by merely
> checking whether the type is empty, without also having to worry
> whether a type was even supplied.
>
> Note that gen_object() in qapi-types still has to check for a
> base, because it is also called for alternates (which have no
> base).

What about the one in gen_visit_struct()?

        if (base and not base.is_empty()) or members:
            ret += mcgen('''
        visit_type_%(c_name)s_fields(v, obj, &err);
    ''',
                         c_name=c_name(name))

> No change to generated code.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: squash in more related changes
> v8: rebase to earlier changes
> v7: rebase to earlier changes
> v6: new patch
> ---
>  scripts/qapi-commands.py                | 17 +++++++------
>  scripts/qapi-event.py                   |  5 ++--
>  scripts/qapi-types.py                   |  4 +--
>  scripts/qapi-visit.py                   | 12 +++++----
>  scripts/qapi.py                         | 25 +++++++++---------
>  tests/qapi-schema/event-case.out        |  2 +-
>  tests/qapi-schema/flat-union-empty.out  |  1 +
>  tests/qapi-schema/ident-with-escape.out |  1 +
>  tests/qapi-schema/indented-expr.out     |  4 +--
>  tests/qapi-schema/qapi-schema-test.out  | 45 ++++++++++++++++++++++++++++++---
>  tests/qapi-schema/union-clash-data.out  |  2 ++
>  tests/qapi-schema/union-empty.out       |  1 +
>  12 files changed, 83 insertions(+), 36 deletions(-)
>
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index 00ee565..9d455c3 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -29,11 +29,11 @@ def gen_call(name, arg_type, ret_type):
>      ret = ''
>
>      argstr = ''
> -    if arg_type:
> -        for memb in arg_type.members:
> -            if memb.optional:
> -                argstr += 'has_%s, ' % c_name(memb.name)
> -            argstr += '%s, ' % c_name(memb.name)
> +    assert arg_type
> +    for memb in arg_type.members:

The assertion doesn't buy us much.  If arg_type is true, but of the
wrong type, the assertion holds, and arg_type.members throws an error.
If it false, then arg_type.members's error is just as useful as an
AssertionError.

More of the same below.

> +        if memb.optional:
> +            argstr += 'has_%s, ' % c_name(memb.name)
> +        argstr += '%s, ' % c_name(memb.name)
>
>      lhs = ''
>      if ret_type:
> @@ -65,7 +65,8 @@ def gen_marshal_vars(arg_type, ret_type):
>  ''',
>                       c_type=ret_type.c_type())
>
> -    if arg_type and not arg_type.is_empty():
> +    assert arg_type
> +    if not arg_type.is_empty():
>          ret += mcgen('''
>      QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
>      QapiDeallocVisitor *qdv;
> @@ -97,7 +98,7 @@ def gen_marshal_vars(arg_type, ret_type):
>  def gen_marshal_input_visit(arg_type, dealloc=False):
>      ret = ''
>
> -    if not arg_type or arg_type.is_empty():
> +    if arg_type.is_empty():
>          return ret
>
>      if dealloc:
> @@ -177,7 +178,7 @@ def gen_marshal(name, arg_type, ret_type):
>
>      # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
>      # for each arg_type member, and by gen_call() for ret_type
> -    if (arg_type and not arg_type.is_empty()) or ret_type:
> +    if not arg_type.is_empty() or ret_type:
>          ret += mcgen('''
>
>  out:
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index 56c93a8..cc55de7 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -39,7 +39,8 @@ def gen_event_send(name, arg_type):
>  ''',
>                  proto=gen_event_send_proto(name, arg_type))
>
> -    if arg_type and not arg_type.is_empty():
> +    assert arg_type
> +    if not arg_type.is_empty():
>          ret += mcgen('''
>      QmpOutputVisitor *qov;
>      Visitor *v;
> @@ -88,7 +89,7 @@ out_obj:
>  ''',
>                   c_enum=c_enum_const(event_enum_name, name))
>
> -    if arg_type and not arg_type.is_empty():
> +    if not arg_type.is_empty():
>          ret += mcgen('''
>  out:
>      qmp_output_visitor_cleanup(qov);
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index c70fae1..5cf20c2 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -58,7 +58,7 @@ struct %(c_name)s {
>  ''',
>                  c_name=c_name(name))
>
> -    if base:
> +    if base and not base.is_empty():
>          ret += mcgen('''
>      /* Members inherited from %(c_name)s: */
>  ''',

Here, you go straight from X to X and not X.is_empty().  Elsewhere, you
take two steps: one patch goes from X to X and X.is_empty(), changing
ouput when X is an empty struct, and this one to just X.is_empty(),
without changing output.

I understand you need to keep checking base because alternates have
none.

I suspect that if base is empty, this hunk changes output, unlike the
other ones, namely the comments

    /* Members inherited from %(c_name)s: */
    /* Own members: */

go away.

If that's true, it should either be a separate patch, or be mentioned in
the commit message.  A test case would be nice.

> @@ -222,7 +222,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
>      def visit_object_type(self, name, info, base, members, variants):
>          self._fwdecl += gen_fwd_object_or_array(name)
>          self.decl += gen_object(name, base, members, variants)
> -        if base:
> +        if not base.is_implicit():

Why .is_implicit() instead of .is_empty() here?

>              self.decl += gen_upcast(name, base)
>          self._gen_type_cleanup(name)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 6537a20..6d5c3d9 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -74,10 +74,11 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
>  def gen_visit_struct_fields(name, base, members):
>      ret = ''
>
> -    if (not base or base.is_empty()) and not members:
> +    assert base
> +    if base.is_empty() and not members:
>          return ret
>
> -    if base and not base.is_empty():
> +    if not base.is_empty():
>          ret += gen_visit_fields_decl(base)
>
>      struct_fields_seen.add(name)
> @@ -90,7 +91,7 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
>  ''',
>                   c_name=c_name(name))
>
> -    if base and not base.is_empty():
> +    if not base.is_empty():
>          ret += mcgen('''
>      visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
>  ''',
> @@ -246,7 +247,8 @@ out:
>  def gen_visit_union(name, base, variants):
>      ret = ''
>
> -    if base:
> +    assert base
> +    if not base.is_empty():
>          ret += gen_visit_fields_decl(base)

Here, you go straight from X to X.is_empty().  Elsewhere, you take two
steps: one patch goes from X to X and X.is_empty(), changing ouput when
X is an empty struct, and this one to just X.is_empty(), without
changing output.

The first step is actually PATCH 29: it changes gen_visit_fields_decl()
not to generate anything for an empty type.

I think it should also make this call unconditional.

>
>      for var in variants.variants:
> @@ -270,7 +272,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>  ''',
>                   c_name=c_name(name))
>
> -    if base:
> +    if not base.is_empty():

Another one, let's see what this does.

>          ret += mcgen('''
>      visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
>  ''',
                        c_name=base.c_name())
       else:
           ret += mcgen('''
       visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
   ''',
                        c_type=variants.tag_member.type.c_name(),
                        c_name=c_name(variants.tag_member.name),
                        name=variants.tag_member.name)

The condition is really flat union (the common members are in the base)
vs. simple union (the common member is tag_member).  If flat union, we
generate the base visit, else the tag_member visit.  Together, this
generates the visit of common members.

Your change breaks the condition: now simple unions have a base, too.
You fix it up to test for empty base instead.  Okay.

> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index a13c110..e9006e4 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -922,11 +922,11 @@ class QAPISchemaArrayType(QAPISchemaType):
>
>  class QAPISchemaObjectType(QAPISchemaType):
>      def __init__(self, name, info, base, local_members, variants):
> -        # struct has local_members, optional base, and no variants
> +        # struct has local_members, base, and no variants
>          # flat union has base, variants, and no local_members
> -        # simple union has local_members, variants, and no base
> +        # simple union has local_members, variants, and base of :empty
>          QAPISchemaType.__init__(self, name, info)
> -        assert base is None or isinstance(base, str)
> +        assert isinstance(base, str) or name == ':empty'

No longer tight: if name == ':empty', base must be None, else it must be
a str.

>          for m in local_members:
>              assert isinstance(m, QAPISchemaObjectTypeMember)
>              m.set_owner(name)
> @@ -1264,11 +1264,11 @@ class QAPISchema(object):
>
>      def _make_implicit_object_type(self, name, info, role, members):
>          if not members:
> -            return None
> +            return ':empty'
>          # See also QAPISchemaObjectTypeMember._pretty_owner()
>          name = ':obj-%s-%s' % (name, role)
>          if not self.lookup_entity(name, QAPISchemaObjectType):
> -            self._def_entity(QAPISchemaObjectType(name, info, None,
> +            self._def_entity(QAPISchemaObjectType(name, info, ':empty',
>                                                    members, None))
>          return name
>
> @@ -1295,7 +1295,7 @@ class QAPISchema(object):
>
>      def _def_struct_type(self, expr, info):
>          name = expr['struct']
> -        base = expr.get('base')
> +        base = expr.get('base', ':empty')
>          data = expr['data']
>          self._def_entity(QAPISchemaObjectType(name, info, base,
>                                                self._make_members(data, info),
> @@ -1315,7 +1315,7 @@ class QAPISchema(object):
>      def _def_union_type(self, expr, info):
>          name = expr['union']
>          data = expr['data']
> -        base = expr.get('base')
> +        base = expr.get('base', ':empty')
>          tag_name = expr.get('discriminator')
>          tag_member = None
>          if tag_name:
> @@ -1349,11 +1349,11 @@ class QAPISchema(object):
>
>      def _def_command(self, expr, info):
>          name = expr['command']
> -        data = expr.get('data')
> +        data = expr.get('data', {})

expr.get('data', {}) returns a str or OrderedDict if expr has the key,
else a dict.  Unclean.

>          rets = expr.get('returns')
>          gen = expr.get('gen', True)
>          success_response = expr.get('success-response', True)
> -        if isinstance(data, OrderedDict):
> +        if isinstance(data, dict):

The test works since OrderedDict is also a dict.

>              data = self._make_implicit_object_type(
>                  name, info, 'arg', self._make_members(data, info))

I remember screwing up dict vs. OrderedDict once, and it failed when
some code really expected an OrderedDict.  _make_members() looks like
it's okay with dict, but why risk it?

Why not simply data = expr.get('data', ':empty') ?

>          if isinstance(rets, list):
> @@ -1364,8 +1364,8 @@ class QAPISchema(object):
>
>      def _def_event(self, expr, info):
>          name = expr['event']
> -        data = expr.get('data')
> -        if isinstance(data, OrderedDict):
> +        data = expr.get('data', {})
> +        if isinstance(data, dict):
>              data = self._make_implicit_object_type(
>                  name, info, 'arg', self._make_members(data, info))
>          self._def_entity(QAPISchemaEvent(name, info, data))

Same here.

> @@ -1612,8 +1612,7 @@ extern const char *const %(c_name)s_lookup[];
>
>
>  def gen_params(arg_type, extra):
> -    if not arg_type:
> -        return extra
> +    assert arg_type
>      assert not arg_type.variants
>      ret = ''
>      sep = ''
[Test output diffs snipped...]

Overall, a somewhat scary change, because all users of base need to be
checked.  The ones you actually changed are easy enough to see for a
reviewer.  Missed ones aren't.  Fortunately, we got a decent test suite.

Not sure this is worth the churn by itself, but let me read further to
see it in more context.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty
  2016-01-25 19:04   ` Markus Armbruster
@ 2016-01-26 16:29     ` Markus Armbruster
  2016-01-27  8:00       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-26 16:29 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
>
>> Now that we elide unnecessary visits of empty types, we can
>> start using the special ':empty' type in more places.  By using
>> the empty type as the base class of every explicit struct or
>> union, and as the default data for any command or event, we can
>> simplify later logic in qapi-{visit,commands,event} by merely
>> checking whether the type is empty, without also having to worry
>> whether a type was even supplied.
>>
>> Note that gen_object() in qapi-types still has to check for a
>> base, because it is also called for alternates (which have no
>> base).
>
> What about the one in gen_visit_struct()?
>
>         if (base and not base.is_empty()) or members:
>             ret += mcgen('''
>         visit_type_%(c_name)s_fields(v, obj, &err);
>     ''',
>                          c_name=c_name(name))
>
>> No change to generated code.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> ---
>> v9: squash in more related changes
>> v8: rebase to earlier changes
>> v7: rebase to earlier changes
>> v6: new patch
>> ---
>>  scripts/qapi-commands.py                | 17 +++++++------
>>  scripts/qapi-event.py                   |  5 ++--
>>  scripts/qapi-types.py                   |  4 +--
>>  scripts/qapi-visit.py                   | 12 +++++----
>>  scripts/qapi.py                         | 25 +++++++++---------
>>  tests/qapi-schema/event-case.out        |  2 +-
>>  tests/qapi-schema/flat-union-empty.out  |  1 +
>>  tests/qapi-schema/ident-with-escape.out |  1 +
>>  tests/qapi-schema/indented-expr.out     |  4 +--
>>  tests/qapi-schema/qapi-schema-test.out  | 45 ++++++++++++++++++++++++++++++---
>>  tests/qapi-schema/union-clash-data.out  |  2 ++
>>  tests/qapi-schema/union-empty.out       |  1 +
>>  12 files changed, 83 insertions(+), 36 deletions(-)

Missing: update to qapi-introspect.py.  At least the expressions like

    arg_type or self._schema.the_empty_object_type

need updating.

I got stuck in reviewing PATCH 31.  The result is probably fine, but the
patch itself is impenetrable for me.  I'm playing with the code to see
whether I can turn that into a constructive remark.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event use of qapi visitor
  2016-01-20 17:10     ` Eric Blake
  2016-01-21  7:16       ` Markus Armbruster
@ 2016-01-26 23:40       ` Eric Blake
  1 sibling, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-26 23:40 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 755 bytes --]

On 01/20/2016 10:10 AM, Eric Blake wrote:
> Oops. It all gets cleaned up in 33 when visit_end_struct() loses the
> errp argument, but in the meantime, I think the most robust way to write
> this would be:
> 
> out_obj:
>     visit_end_struct(v, err ? NULL : &err);
>     if (err) {
> ...
> 
>>
>> I guess the idea is to go from gen_visit_fields() failure through
>> visit_end_struct() here to out.  Correct?
> 
> Yes.

Technically, visit_end_struct() for QMP output visitor never sets errp,
but it's not nice to have to audit multiple files just to find that out.
 I'll stick to the ternary until patch 33 cleans it back up.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty
  2016-01-26 16:29     ` Markus Armbruster
@ 2016-01-27  8:00       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27  8:00 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Markus Armbruster <armbru@redhat.com> writes:
>
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> Now that we elide unnecessary visits of empty types, we can
>>> start using the special ':empty' type in more places.  By using
>>> the empty type as the base class of every explicit struct or
>>> union, and as the default data for any command or event, we can
>>> simplify later logic in qapi-{visit,commands,event} by merely
>>> checking whether the type is empty, without also having to worry
>>> whether a type was even supplied.

You rewrite a command's arg_type from None to ':empty', bit not its
ret_type.  Deepens the assymmetry between the two.

>>> Note that gen_object() in qapi-types still has to check for a
>>> base, because it is also called for alternates (which have no
>>> base).
>>
>> What about the one in gen_visit_struct()?
>>
>>         if (base and not base.is_empty()) or members:
>>             ret += mcgen('''
>>         visit_type_%(c_name)s_fields(v, obj, &err);
>>     ''',
>>                          c_name=c_name(name))
>>
>>> No change to generated code.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>>> ---
>>> v9: squash in more related changes
>>> v8: rebase to earlier changes
>>> v7: rebase to earlier changes
>>> v6: new patch
>>> ---
>>>  scripts/qapi-commands.py                | 17 +++++++------
>>>  scripts/qapi-event.py                   |  5 ++--
>>>  scripts/qapi-types.py                   |  4 +--
>>>  scripts/qapi-visit.py                   | 12 +++++----
>>>  scripts/qapi.py                         | 25 +++++++++---------
>>>  tests/qapi-schema/event-case.out        |  2 +-
>>>  tests/qapi-schema/flat-union-empty.out  |  1 +
>>>  tests/qapi-schema/ident-with-escape.out |  1 +
>>>  tests/qapi-schema/indented-expr.out     |  4 +--
>>>  tests/qapi-schema/qapi-schema-test.out  | 45 ++++++++++++++++++++++++++++++---
>>>  tests/qapi-schema/union-clash-data.out  |  2 ++
>>>  tests/qapi-schema/union-empty.out       |  1 +
>>>  12 files changed, 83 insertions(+), 36 deletions(-)
>
> Missing: update to qapi-introspect.py.  At least the expressions like
>
>     arg_type or self._schema.the_empty_object_type
>
> need updating.

But so far not the ret_type or self._schema.the_empty_object_type.

[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
  2016-01-22 19:24   ` Markus Armbruster
@ 2016-01-27 13:54   ` Markus Armbruster
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
                       ` (2 more replies)
  1 sibling, 3 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 13:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

I got stuck reviewing "[PATCH v9 31/37] qapi-visit: Unify struct and
union visit".  The result is probably fine, but the patch itself is
impenetrable for me.  Here's my version.  I believe it's really simple
to review (but as the author, I'm hopelessly biased).

I based on PATCH 26/37 instead of PATCH 30, because I found the empty
struct stuff from PACTH 27-30 not useful for this unification job.  If
we want it for other reasons (I have no opinion, yet), rebasing it
onto this series shouldn't be hard.

Markus Armbruster (3):
  qapi-visit: Simplify how we visit common union members
  qapi-visit: Clean up code generated around visit_end_union()
  qapi-visit: Unify struct and union visit

 scripts/qapi-visit.py | 122 +++++++++++++++-----------------------------------
 1 file changed, 36 insertions(+), 86 deletions(-)

-- 
2.4.3

^ permalink raw reply	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members
  2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
@ 2016-01-27 13:54     ` Markus Armbruster
  2016-01-27 21:48       ` Eric Blake
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union() Markus Armbruster
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 3/3] qapi-visit: Unify struct and union visit Markus Armbruster
  2 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 13:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

For a simple union SU, gen_visit_union() generates a visit of its
single tag member, like this:

    visit_type_SUKind(v, "type", &(*obj)->type, &err);

For a flat union FU with base B, it generates a visit of its base
fields:

    visit_type_B_fields(v, (B **)obj, &err);

Instead, we can simply visit the common members using the same fields
visit function we use for structs, generated with
gen_visit_struct_fields().  This function visits the base if any, then
the local members.

For a simple union SU, visit_type_SU_fields() contains exactly the old
tag member visit, because there is no base, and the tag member is the
only member.  For instance, the code generated for
qapi-schema-test.json's UserDefNativeListUnion changes like this:

    @@ -740,6 +766,19 @@ out:
	 error_propagate(errp, err);
     }

    +static void visit_type_UserDefNativeListUnion_fields(Visitor *v, UserDefNativeListUnion **obj, Error **errp)
    +{
    +    Error *err = NULL;
    +
    +    visit_type_UserDefNativeListUnionKind(v, "type", &(*obj)->type, &err);
    +    if (err) {
    +        goto out;
    +    }
    +
    +out:
    +    error_propagate(errp, err);
    +}
    +
     void visit_type_UserDefNativeListUnion(Visitor *v, const char *name, UserDefNativeListUnion **obj, Error **errp)
     {
	 Error *err = NULL;
    @@ -751,7 +790,7 @@ void visit_type_UserDefNativeListUnion(V
	 if (!*obj) {
	     goto out_obj;
	 }
    -    visit_type_UserDefNativeListUnionKind(v, "type", &(*obj)->type, &err);
    +    visit_type_UserDefNativeListUnion_fields(v, obj, &err);
	 if (err) {
	     goto out_obj;
	 }

For a flat union FU, visit_type_FU_fields() contains exactly the old
base fields visit, because there is a base, but no members.  For
instance, the code generated for qapi-schema-test.json's
UserDefFlatUnion changes like this:

    @@ -616,6 +616,19 @@ out:

     static void visit_type_UserDefUnionBase_fields(Visitor *v, UserDefUnionBase **obj, Error **errp);

    +static void visit_type_UserDefFlatUnion_fields(Visitor *v, UserDefFlatUnion **obj, Error **errp)
    +{
    +    Error *err = NULL;
    +
    +    visit_type_UserDefUnionBase_fields(v, (UserDefUnionBase **)obj, &err);
    +    if (err) {
    +        goto out;
    +    }
    +
    +out:
    +    error_propagate(errp, err);
    +}
    +
     static void visit_type_implicit_UserDefA(Visitor *v, UserDefA **obj, Error **errp)
     {
	 Error *err = NULL;
    @@ -651,7 +664,7 @@ void visit_type_UserDefFlatUnion(Visitor
	 if (!*obj) {
	     goto out_obj;
	 }
    -    visit_type_UserDefUnionBase_fields(v, (UserDefUnionBase **)obj, &err);
    +    visit_type_UserDefFlatUnion_fields(v, obj, &err);
	 if (err) {
	     goto out_obj;
	 }

As you see, the generated code grows a bit, but in practice, it's lost
in the noise: qapi-schema.json's qapi-visit.c gains roughly 1%.

This simplification became possible with commit 441cbac "qapi-visit:
Convert to QAPISchemaVisitor, fixing bugs".  It's a step towards
unifying gen_struct() and gen_union().

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-visit.py | 27 ++++-----------------------
 1 file changed, 4 insertions(+), 23 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 573bb81..5c0d4d2 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -238,11 +238,8 @@ out:
     return ret
 
 
-def gen_visit_union(name, base, variants):
-    ret = ''
-
-    if base:
-        ret += gen_visit_fields_decl(base)
+def gen_visit_union(name, base, members, variants):
+    ret = gen_visit_struct_fields(name, base, members)
 
     for var in variants.variants:
         # Ugly special case for simple union TODO get rid of it
@@ -262,21 +259,9 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     if (!*obj) {
         goto out_obj;
     }
+    visit_type_%(c_name)s_fields(v, obj, &err);
 ''',
                  c_name=c_name(name))
-
-    if base:
-        ret += mcgen('''
-    visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
-''',
-                     c_name=base.c_name())
-    else:
-        ret += mcgen('''
-    visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
-''',
-                     c_type=variants.tag_member.type.c_name(),
-                     c_name=c_name(variants.tag_member.name),
-                     name=variants.tag_member.name)
     ret += gen_err_check(label='out_obj')
     ret += mcgen('''
     if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
@@ -381,11 +366,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
     def visit_object_type(self, name, info, base, members, variants):
         self.decl += gen_visit_decl(name)
         if variants:
-            if members:
-                # Members other than variants.tag_member not implemented
-                assert len(members) == 1
-                assert members[0] == variants.tag_member
-            self.defn += gen_visit_union(name, base, variants)
+            self.defn += gen_visit_union(name, base, members, variants)
         else:
             self.defn += gen_visit_struct(name, base, members)
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union()
  2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
@ 2016-01-27 13:54     ` Markus Armbruster
  2016-01-27 14:02       ` Eric Blake
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 3/3] qapi-visit: Unify struct and union visit Markus Armbruster
  2 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 13:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

The generated code can call visit_end_union() without having called
visit_start_union().  Example:

	if (!*obj) {
	    goto out_obj;
	}
	visit_type_BlockdevOptions_fields(v, obj, &err);
	if (err) {
	    goto out_obj; // if we go from here...
	}
	if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
	    goto out_obj;
	}
	switch ((*obj)->driver) {
    [...]
	}
    out_obj:
        // ... then *obj is true, and ...
	error_propagate(errp, err);
	err = NULL;
	if (*obj) {
	    // we end up here
	    visit_end_union(v, !!(*obj)->u.data, &err);
	}
	error_propagate(errp, err);

Harmless only because no visitor implements end_union().  Clean it up
anyway.

Messed up since we have visit_end_union (commit cee2ded).

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-visit.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 5c0d4d2..8dcc6dc 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -299,12 +299,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     default:
         abort();
     }
-out_obj:
     error_propagate(errp, err);
     err = NULL;
-    if (*obj) {
-        visit_end_union(v, !!(*obj)->u.data, &err);
-    }
+    visit_end_union(v, !!(*obj)->u.data, &err);
+out_obj:
     error_propagate(errp, err);
     err = NULL;
     visit_end_struct(v, &err);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* [Qemu-devel] [PATCH 3/3] qapi-visit: Unify struct and union visit
  2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union() Markus Armbruster
@ 2016-01-27 13:54     ` Markus Armbruster
  2 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 13:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: marcandre.lureau, mdroth

gen_visit_union() is now just like gen_visit_struct() plus additional
code to handle variants.  Make that code conditional on variants, so
gen_visit_union() does exactly the same for structs as
gen_visit_struct().  Rename it to gen_visit_object(), use it for
structs, and drop gen_visit_struct().

Output is identical.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi-visit.py | 95 ++++++++++++++++++---------------------------------
 1 file changed, 33 insertions(+), 62 deletions(-)

diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 8dcc6dc..9e044c2 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -109,40 +109,6 @@ out:
     return ret
 
 
-def gen_visit_struct(name, base, members):
-    ret = gen_visit_struct_fields(name, base, members)
-
-    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
-    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
-    # rather than leaving it non-NULL. As currently written, the caller must
-    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
-    ret += mcgen('''
-
-void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
-{
-    Error *err = NULL;
-
-    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
-    if (err) {
-        goto out;
-    }
-    if (!*obj) {
-        goto out_obj;
-    }
-    visit_type_%(c_name)s_fields(v, obj, &err);
-out_obj:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, &err);
-out:
-    error_propagate(errp, err);
-}
-''',
-                 c_name=c_name(name))
-
-    return ret
-
-
 def gen_visit_list(name, element_type):
     # FIXME: if *obj is NULL on entry, and the first visit_next_list()
     # assigns to *obj, while a later one fails, we should clean up *obj
@@ -238,14 +204,19 @@ out:
     return ret
 
 
-def gen_visit_union(name, base, members, variants):
+def gen_visit_object(name, base, members, variants):
     ret = gen_visit_struct_fields(name, base, members)
 
-    for var in variants.variants:
-        # Ugly special case for simple union TODO get rid of it
-        if not var.simple_union_type():
-            ret += gen_visit_implicit_struct(var.type)
+    if variants:
+        for var in variants.variants:
+            # Ugly special case for simple union TODO get rid of it
+            if not var.simple_union_type():
+                ret += gen_visit_implicit_struct(var.type)
 
+    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
+    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
+    # rather than leaving it non-NULL. As currently written, the caller must
+    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
     ret += mcgen('''
 
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
@@ -262,46 +233,49 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     visit_type_%(c_name)s_fields(v, obj, &err);
 ''',
                  c_name=c_name(name))
-    ret += gen_err_check(label='out_obj')
-    ret += mcgen('''
+    if variants:
+        ret += gen_err_check(label='out_obj')
+        ret += mcgen('''
     if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
         goto out_obj;
     }
     switch ((*obj)->%(c_name)s) {
 ''',
-                 c_name=c_name(variants.tag_member.name))
+                     c_name=c_name(variants.tag_member.name))
 
-    for var in variants.variants:
-        # TODO ugly special case for simple union
-        simple_union_type = var.simple_union_type()
-        ret += mcgen('''
+        for var in variants.variants:
+            # TODO ugly special case for simple union
+            simple_union_type = var.simple_union_type()
+            ret += mcgen('''
     case %(case)s:
 ''',
-                     case=c_enum_const(variants.tag_member.type.name,
-                                       var.name))
-        if simple_union_type:
-            ret += mcgen('''
+                         case=c_enum_const(variants.tag_member.type.name,
+                                           var.name))
+            if simple_union_type:
+                ret += mcgen('''
         visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
 ''',
-                         c_type=simple_union_type.c_name(),
-                         c_name=c_name(var.name))
-        else:
-            ret += mcgen('''
+                             c_type=simple_union_type.c_name(),
+                             c_name=c_name(var.name))
+            else:
+                ret += mcgen('''
         visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
 ''',
-                         c_type=var.type.c_name(),
-                         c_name=c_name(var.name))
-        ret += mcgen('''
+                             c_type=var.type.c_name(),
+                             c_name=c_name(var.name))
+            ret += mcgen('''
         break;
 ''')
 
-    ret += mcgen('''
+        ret += mcgen('''
     default:
         abort();
     }
     error_propagate(errp, err);
     err = NULL;
     visit_end_union(v, !!(*obj)->u.data, &err);
+''')
+    ret += mcgen('''
 out_obj:
     error_propagate(errp, err);
     err = NULL;
@@ -363,10 +337,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
 
     def visit_object_type(self, name, info, base, members, variants):
         self.decl += gen_visit_decl(name)
-        if variants:
-            self.defn += gen_visit_union(name, base, members, variants)
-        else:
-            self.defn += gen_visit_struct(name, base, members)
+        self.defn += gen_visit_object(name, base, members, variants)
 
     def visit_alternate_type(self, name, info, variants):
         self.decl += gen_visit_decl(name)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union()
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union() Markus Armbruster
@ 2016-01-27 14:02       ` Eric Blake
  2016-01-27 14:46         ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-27 14:02 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth

[-- Attachment #1: Type: text/plain, Size: 1332 bytes --]

On 01/27/2016 06:54 AM, Markus Armbruster wrote:
> The generated code can call visit_end_union() without having called
> visit_start_union().  Example:
> 
> 	if (!*obj) {
> 	    goto out_obj;
> 	}
> 	visit_type_BlockdevOptions_fields(v, obj, &err);
> 	if (err) {
> 	    goto out_obj; // if we go from here...
> 	}
> 	if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
> 	    goto out_obj;
> 	}
> 	switch ((*obj)->driver) {
>     [...]
> 	}
>     out_obj:
>         // ... then *obj is true, and ...
> 	error_propagate(errp, err);
> 	err = NULL;
> 	if (*obj) {
> 	    // we end up here
> 	    visit_end_union(v, !!(*obj)->u.data, &err);
> 	}
> 	error_propagate(errp, err);
> 
> Harmless only because no visitor implements end_union().  Clean it up
> anyway.

I plan on deleting visit_end_union() anyways (and visit_start_union);
see 32/37, plus the FIXME comments added in 21/37.  Maybe it's easier to
just delete this incorrect and unused callback earlier in the series,
using your commit message as additional rationale why it is worthless,
and leaving only visit_start_union() cleanups for 32/37.

> 
> Messed up since we have visit_end_union (commit cee2ded).

Indeed.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit Eric Blake
@ 2016-01-27 14:12   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 14:12 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

I'm sending this out even though it's unfinished.  It's probably more
useful for documenting my difficulties and confusion than for improving
the code.

Eric Blake <eblake@redhat.com> writes:

> We are finally at the point where gen_visit_struct() and
> gen_visit_union() can be unified to a generic gen_visit_object().
>
> The generated code for structs and for flat unions is unchanged.
> For simple unions, a new visit_type_FOO_fields() is created,
> wrapping the visit of the non-variant tag field:
>
> |+static void visit_type_ChardevBackend_fields(Visitor *v, ChardevBackend **obj, Error **errp)
> |+{
> |+    Error *err = NULL;
> |+
> |+    visit_type_ChardevBackendKind(v, "type", &(*obj)->type, &err);
> |+    if (err) {
> |+        goto out;
> |+    }
> |+
> |+out:
> |+    error_propagate(errp, err);
> |+}
> |+
> | void visit_type_ChardevBackend(Visitor *v, const char *name, ChardevBackend **obj, Error **errp)
> | {
> |     Error *err = NULL;
> |@@ -2319,7 +2332,7 @@ void visit_type_ChardevBackend(Visitor *
> |     if (!*obj) {
> |         goto out_obj;
> |     }
> |-    visit_type_ChardevBackendKind(v, "type", &(*obj)->type, &err);
> |+    visit_type_ChardevBackend_fields(v, obj, &err);
> |     if (err) {
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to 'name' motion
> v7: rebase to earlier changes
> v6: new patch
> ---
>  scripts/qapi-visit.py | 133 +++++++++++++++++++-------------------------------
>  1 file changed, 51 insertions(+), 82 deletions(-)
>
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 6d5c3d9..feef17f 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -109,46 +109,6 @@ out:
>      return ret
>
>
> -def gen_visit_struct(name, base, members):
> -    ret = gen_visit_struct_fields(name, base, members)
> -
> -    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
> -    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
> -    # rather than leaving it non-NULL. As currently written, the caller must
> -    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
> -    ret += mcgen('''
> -
> -void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
> -{
> -    Error *err = NULL;
> -
> -    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
> -    if (err) {
> -        goto out;
> -    }
> -    if (!*obj) {
> -        goto out_obj;
> -    }
> -''',
> -                 name=name, c_name=c_name(name))
> -    if (base and not base.is_empty()) or members:
> -        ret += mcgen('''
> -    visit_type_%(c_name)s_fields(v, obj, &err);
> -''',
> -                     c_name=c_name(name))
> -    ret += mcgen('''
> -out_obj:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> -out:
> -    error_propagate(errp, err);
> -}
> -''')
> -
> -    return ret
> -
> -
>  def gen_visit_list(name, element_type):
>      # FIXME: if *obj is NULL on entry, and the first visit_next_list()
>      # assigns to *obj, while a later one fails, we should clean up *obj
> @@ -244,18 +204,24 @@ out:
>      return ret
>

You're merging gen_visit_struct() and gen_visit_union() into
gen_visit_object().  Need to review both the change from
gen_visit_struct() to gen_visit_object(), and from gen_visit_union() to
gen_visit_object().  Diff shows the latter, but not the former.  A
manual diff of the old gen_visit_struct() and new gen_visit_union()
doesn't come out helpful.

Recap differences between structs, flat unions and simple unions:

                    base           members        variants
    struct          may be empty   may be empty   no
    flat union      non-empty      empty          yes
    simple union    empty          just the tag   yes

>
> -def gen_visit_union(name, base, variants):
> +def gen_visit_object(name, base, members, variants):
>      ret = ''
>
>      assert base
>      if not base.is_empty():
>          ret += gen_visit_fields_decl(base)
> +    if members:
> +        ret += gen_visit_struct_fields(name, base, members)

Flat unions have no members: the new code does nothing.

Simple unions have a single member: we now generate a
visit_type_UNION_fields() for it.

Respective part of gen_visit_struct():

       ret = gen_visit_struct_fields(name, base, members)

It looks like you add a gen_visit_fields_decl() when the struct's base
is non-empty.  You actually don't, because the additional one in
gen_visit_object() suppresses the one in gen_visit_struct_fields().  I
think.

> +    if variants:
> +        for var in variants.variants:
> +            # Ugly special case for simple union TODO get rid of it
> +            if not var.simple_union_type():
> +                ret += gen_visit_implicit_struct(var.type)
>
> -    for var in variants.variants:
> -        # Ugly special case for simple union TODO get rid of it
> -        if not var.simple_union_type():
> -            ret += gen_visit_implicit_struct(var.type)
> -

Unions always have variants: no change.

Struct's don't: no change.

> +    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
> +    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
> +    # rather than leaving it non-NULL. As currently written, the caller must
> +    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.

Comment inherited from gen_visit_struct().  Does it apply to unions as
well?

>      ret += mcgen('''
>
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
> @@ -272,61 +238,71 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>  ''',
>                   c_name=c_name(name))
>
> -    if not base.is_empty():
> +    if not base.is_empty() or members:
> +        if members:
> +            type_name = c_name(name)
> +            cast = ''
> +        else:
> +            type_name = base.c_name()
> +            cast = '(%s **)' % type_name
>          ret += mcgen('''
> -    visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
> +    visit_type_%(c_name)s_fields(v, %(cast)sobj, &err);
>  ''',
> -                     c_name=base.c_name())
> -    else:
> +                     c_name=type_name, cast=cast)
> +        if variants:
> +            ret += gen_err_check(label='out_obj')
> +
> +    if variants:
>          ret += mcgen('''
> -    visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
> -''',
> -                     c_type=variants.tag_member.type.c_name(),
> -                     c_name=c_name(variants.tag_member.name),
> -                     name=variants.tag_member.name)
> -    ret += gen_err_check(label='out_obj')

Around here, the diff becomes too hard to read for me.

Please have a look at my "[PATCH 0/3] qapi-visit: Unify struct and union
visit".

> -    ret += mcgen('''
>      if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
>          goto out_obj;
>      }
>      switch ((*obj)->%(c_name)s) {
>  ''',
> -                 c_name=c_name(variants.tag_member.name))
> +                     c_name=c_name(variants.tag_member.name))
>
> -    for var in variants.variants:
> -        # TODO ugly special case for simple union
> -        simple_union_type = var.simple_union_type()
> -        ret += mcgen('''
> +        for var in variants.variants:
> +            # TODO ugly special case for simple union
> +            simple_union_type = var.simple_union_type()
> +            ret += mcgen('''
>      case %(case)s:
>  ''',
> -                     case=c_enum_const(variants.tag_member.type.name,
> -                                       var.name))
> -        if simple_union_type:
> -            ret += mcgen('''
> +                         case=c_enum_const(variants.tag_member.type.name,
> +                                           var.name))
> +            if simple_union_type:
> +                ret += mcgen('''
>          visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
>  ''',
> -                         c_type=simple_union_type.c_name(),
> -                         c_name=c_name(var.name))
> -        elif not var.type.is_empty():
> -            ret += mcgen('''
> +                             c_type=simple_union_type.c_name(),
> +                             c_name=c_name(var.name))
> +            elif not var.type.is_empty():
> +                ret += mcgen('''
>          visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
>  ''',
> -                         c_type=var.type.c_name(),
> -                         c_name=c_name(var.name))
> -        ret += mcgen('''
> +                             c_type=var.type.c_name(),
> +                             c_name=c_name(var.name))
> +            ret += mcgen('''
>          break;
>  ''')
>
> -    ret += mcgen('''
> +        ret += mcgen('''
>      default:
>          abort();
>      }
> +''')
> +
> +    ret += mcgen('''
>  out_obj:
> +''')
> +    if variants:
> +        ret += mcgen('''
>      error_propagate(errp, err);
>      err = NULL;
>      if (*obj) {
>          visit_end_union(v, !!(*obj)->u.data, &err);
>      }
> +''')
> +    ret += mcgen('''
>      error_propagate(errp, err);
>      err = NULL;
>      visit_end_struct(v, &err);
> @@ -387,14 +363,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
>
>      def visit_object_type(self, name, info, base, members, variants):
>          self.decl += gen_visit_decl(name)
> -        if variants:
> -            if members:
> -                # Members other than variants.tag_member not implemented
> -                assert len(members) == 1
> -                assert members[0] == variants.tag_member
> -            self.defn += gen_visit_union(name, base, variants)
> -        else:
> -            self.defn += gen_visit_struct(name, base, members)
> +        self.defn += gen_visit_object(name, base, members, variants)
>
>      def visit_alternate_type(self, name, info, variants):
>          self.decl += gen_visit_decl(name)

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union()
  2016-01-27 14:02       ` Eric Blake
@ 2016-01-27 14:46         ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 14:46 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, mdroth

Eric Blake <eblake@redhat.com> writes:

> On 01/27/2016 06:54 AM, Markus Armbruster wrote:
>> The generated code can call visit_end_union() without having called
>> visit_start_union().  Example:
>> 
>> 	if (!*obj) {
>> 	    goto out_obj;
>> 	}
>> 	visit_type_BlockdevOptions_fields(v, obj, &err);
>> 	if (err) {
>> 	    goto out_obj; // if we go from here...
>> 	}
>> 	if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
>> 	    goto out_obj;
>> 	}
>> 	switch ((*obj)->driver) {
>>     [...]
>> 	}
>>     out_obj:
>>         // ... then *obj is true, and ...
>> 	error_propagate(errp, err);
>> 	err = NULL;
>> 	if (*obj) {
>> 	    // we end up here
>> 	    visit_end_union(v, !!(*obj)->u.data, &err);
>> 	}
>> 	error_propagate(errp, err);

Tabs crept into my commit message, oops.

>> 
>> Harmless only because no visitor implements end_union().  Clean it up
>> anyway.
>
> I plan on deleting visit_end_union() anyways (and visit_start_union);
> see 32/37, plus the FIXME comments added in 21/37.  Maybe it's easier to
> just delete this incorrect and unused callback earlier in the series,
> using your commit message as additional rationale why it is worthless,
> and leaving only visit_start_union() cleanups for 32/37.

I could've tried to move PATCH 32 before the unification, but that
would've been work, so I went with this straightforward fix instead.
Sure, it fixes something that'll go away soon, but I think the churn is
quite tolerable: 1 file changed, 2 insertions(+), 4 deletions(-).

>> Messed up since we have visit_end_union (commit cee2ded).
>
> Indeed.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct Eric Blake
@ 2016-01-27 16:41   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 16:41 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Commit cee2dedb noticed that if you have a partial flat union
> (such as if an input parse failed due to a missing
> discriminator), calling the dealloc visitor could result in
> trying to dereference the NULL pointer. But the fix it proposed
> requires the use of a 'data' member in the union, which may or
> may not be the same size as other branches of the union
> (consider a 32-bit platform where one of the branches is an
> int64), so it feels fairly dirty.  A better fix is to tweak all
> of the generated visit_type_implicit_FOO() functions to avoid
> dereferencing NULL in the first place, by not visiting the
> fields if the struct pointer itself is not present, at which
> point we no longer even need visit_start_union().  And no one
> was implementing visit_end_union() callbacks.
>
> While rewriting the code, use patterns that are closer to what
> is used elsewhere in the generated visitors, by using 'goto'
> to cleanup labels rather than putting followup code under 'if'
> conditions.  The change keeps the contract that any successful
> use of visit_start_implicit_struct() will be paired with a
> matching visit_end_implicit_struct(), even if intermediate
> processing is skipped.  We are safe in checking *obj alone, as
> as the contract of visit_start_implicit_struct() requires a
> non-NULL obj.
>
> As an example of the changes to generated code:

This could be easier to understand if you show the change to the union
visit (hunks 2+3) before the change to one of its variant members (hunk
1).

> |@@ -1331,10 +1331,16 @@ static void visit_type_implicit_Blockdev
> |     Error *err = NULL;
> |
> |     visit_start_implicit_struct(v, (void **)obj, sizeof(BlockdevOptionsArchipelago), &err);
> |-    if (!err) {
> |-        visit_type_BlockdevOptionsArchipelago_fields(v, obj, errp);
> |-        visit_end_implicit_struct(v);
> |+    if (err) {
> |+        goto out;
> |+    }
> |+    if (!*obj) {
> |+        goto out_obj;
> |     }
> |+    visit_type_BlockdevOptionsArchipelago_fields(v, obj, &err);
> |+out_obj:
> |+    visit_end_implicit_struct(v);
> |+out:
> |     error_propagate(errp, err);
> | }
> ...
> |@@ -1479,9 +1539,6 @@ void visit_type_BlockdevOptions(Visitor
> |     if (err) {
> |         goto out_obj;
> |     }
> |-    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
> |-        goto out_obj;
> |-    }

If v is the dealloc visitor, the condition is !(*obj)->u.data.
Else, it's false.

(*obj)->u.data can be null only for a partially initialized obj.

Spelling out the obvious: partially initialized objects may be visited
with the dealloc visitor only.

So, this basically boils down to "if we're deallocating a partially
initialized object, and the variant part hasn't been initialized, bypass
the switch visiting the variant part.

Your patch changes it to visit the variant part unconditionally.  The
code doing that visit needs to be able to cope with an uninitialized
part.

> |     switch ((*obj)->driver) {

Which variant do we visit?  If the tag hasn't been initialized, we
arbitrarily visit the first one.  Amazingly, this actually works, as we
shall see.

> |     case BLOCKDEV_DRIVER_ARCHIPELAGO:
> |         visit_type_implicit_BlockdevOptionsArchipelago(v, &(*obj)->u.archipelago, &err);

Note that (*obj)->u.archipelago equals (*obj)->u.data by construction of
the union's C data type: the variant members are all stored boxed.

So, when (and only when, I think) we visit an uninitialized variant, the
visit function's obj parameter points to a null pointer.

            break;
        [More cases...]
> |@@ -1570,11 +1627,6 @@ void visit_type_BlockdevOptions(Visitor
        default:
            abort();
        }
> | out_obj:
> |     error_propagate(errp, err);
> |     err = NULL;
> |-    if (*obj) {
> |-        visit_end_union(v, !!(*obj)->u.data, &err);
> |-    }
> |-    error_propagate(errp, err);
> |-    err = NULL;
> |     visit_end_struct(v, &err);

Now let's see how function to visit the variant part changes.  Before:

    static void visit_type_implicit_BlockdevOptionsArchipelago(Visitor *v, BlockdevOptionsArchipelago **obj, Error **errp)
    {
        Error *err = NULL;

        visit_start_implicit_struct(v, (void **)obj, sizeof(BlockdevOptionsArchipelago), &err);
        if (!err) {
            visit_type_BlockdevOptionsArchipelago_fields(v, obj, errp);
            visit_end_implicit_struct(v);
        }
        error_propagate(errp, err);
    }

After:

    static void visit_type_implicit_BlockdevOptionsArchipelago(Visitor *v, BlockdevOptionsArchipelago **obj, Error **errp)
    {
        Error *err = NULL;

        visit_start_implicit_struct(v, (void **)obj, sizeof(BlockdevOptionsArchipelago), &err);
        if (err) {
            goto out;
        }
        if (!*obj) {
            goto out_obj;
        }
        visit_type_BlockdevOptionsArchipelago_fields(v, obj, &err);
    out_obj:
        visit_end_implicit_struct(v);
    out:
        error_propagate(errp, err);
    }

If err, no change.

Else if !*obj, i.e. we're visiting an uninitialized part with the
dealloc visitor, we now skip visiting the fields.  We still call
visit_start_implicit_struct() and visit_end_implicit_struct().

Else no change.

Taken together: when we visit a partially initialized union...

* The actual visitor must be the dealloc visitor then.

* We now execute visit_end_implicit_struct() and
  visit_end_implicit_struct().  If the tag has been initialized, we
  execute them for the part belonging to the tag value.  Else, we
  arbitrarily execute them for the part belonging to the value 0.

* The dealloc visitor's qapi_dealloc_start_implicit_struct() stores its
  obj argument for later.

  It's qapi_dealloc_end_implicit_struct() retrieves the stored obj and
  frees *obj.

  For the calls added by this patch, this is a no-op, because *obj is
  null.

* In all the cases, the additionally executed code does nothing.

Works.  The "arbitrarily execute them for tag value 0" bit is quite
ugly, though.

Aside: if we unbox the variant parts, start_implicit_struct() and
end_implicit_struct() should go away, and then things become less ugly.

> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: rebase to 'name' motion
> v7: rebase to earlier context changes, simplify 'obj && !*obj'
> condition based on contract
> v6: rebase due to deferring 7/46, and gen_err_check() improvements;
> rewrite gen_visit_implicit_struct() more like other patterns
> ---
>  include/qapi/visitor-impl.h |  5 -----
>  include/qapi/visitor.h      | 12 ------------
>  qapi/qapi-dealloc-visitor.c | 26 --------------------------
>  qapi/qapi-visit-core.c      | 15 ---------------
>  scripts/qapi-visit.py       | 25 +++++++++----------------
>  5 files changed, 9 insertions(+), 74 deletions(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 913f1b0..3b68b7b 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -81,11 +81,6 @@ struct Visitor
>
>      /* May be NULL; most useful for input visitors. */
>      void (*optional)(Visitor *v, const char *name, bool *present);
> -
> -    /* FIXME - needs to be removed */
> -    bool (*start_union)(Visitor *v, bool data_present, Error **errp);
> -    /* FIXME - needs to be removed */
> -    void (*end_union)(Visitor *v, bool data_present, Error **errp);
>  };
>
>  /**
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 6e49b51..d7a0110 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -274,16 +274,4 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
>   */
>  void visit_type_null(Visitor *v, const char *name, Error **errp);
>
> -/**
> - * Mark the start of visiting the branches of a union. Return true if
> - * @data_present.
> - * FIXME: Should not be needed
> - */
> -bool visit_start_union(Visitor *v, bool data_present, Error **errp);
> -/**
> - * Mark the end of union branches, after visit_start_union().
> - * FIXME: Should not be needed
> - */
> -void visit_end_union(Visitor *v, bool data_present, Error **errp);
> -
>  #endif
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index ede1703..1a54864 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -171,31 +171,6 @@ static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
>  {
>  }
>
> -/* If there's no data present, the dealloc visitor has nothing to free.
> - * Thus, indicate to visitor code that the subsequent union fields can
> - * be skipped. This is not an error condition, since the cleanup of the
> - * rest of an object can continue unhindered, so leave errp unset in
> - * these cases.
> - *
> - * NOTE: In cases where we're attempting to deallocate an object that
> - * may have missing fields, the field indicating the union type may
> - * be missing. In such a case, it's possible we don't have enough
> - * information to differentiate data_present == false from a case where
> - * data *is* present but happens to be a scalar with a value of 0.
> - * This is okay, since in the case of the dealloc visitor there's no
> - * work that needs to done in either situation.
> - *
> - * The current inability in QAPI code to more thoroughly verify a union
> - * type in such cases will likely need to be addressed if we wish to
> - * implement this interface for other types of visitors in the future,
> - * however.
> - */
> -static bool qapi_dealloc_start_union(Visitor *v, bool data_present,
> -                                     Error **errp)
> -{
> -    return data_present;
> -}
> -
>  Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
>  {
>      return &v->visitor;
> @@ -227,7 +202,6 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
>      v->visitor.type_number = qapi_dealloc_type_number;
>      v->visitor.type_any = qapi_dealloc_type_anything;
>      v->visitor.type_null = qapi_dealloc_type_null;
> -    v->visitor.start_union = qapi_dealloc_start_union;
>
>      QTAILQ_INIT(&v->stack);
>
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 399256b..3360cdf 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -76,21 +76,6 @@ void visit_end_list(Visitor *v)
>      v->end_list(v);
>  }
>
> -bool visit_start_union(Visitor *v, bool data_present, Error **errp)
> -{
> -    if (v->start_union) {
> -        return v->start_union(v, data_present, errp);
> -    }
> -    return true;
> -}
> -
> -void visit_end_union(Visitor *v, bool data_present, Error **errp)
> -{
> -    if (v->end_union) {
> -        v->end_union(v, data_present, errp);
> -    }
> -}
> -
>  bool visit_optional(Visitor *v, const char *name, bool *present)
>  {
>      if (v->optional) {
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index feef17f..7a44c13 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -60,10 +60,16 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
>      Error *err = NULL;
>
>      visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
> -    if (!err) {
> -        visit_type_%(c_type)s_fields(v, obj, errp);
> -        visit_end_implicit_struct(v);
> +    if (err) {
> +        goto out;
>      }
> +    if (!*obj) {
> +        goto out_obj;
> +    }
> +    visit_type_%(c_type)s_fields(v, obj, &err);
> +out_obj:
> +    visit_end_implicit_struct(v);
> +out:
>      error_propagate(errp, err);
>  }
>  ''',

Matches the diff in the commit message.

> @@ -254,9 +260,6 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>
>      if variants:
>          ret += mcgen('''
> -    if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
> -        goto out_obj;
> -    }
>      switch ((*obj)->%(c_name)s) {
>  ''',
>                       c_name=c_name(variants.tag_member.name))

Likewise.

> @@ -293,16 +296,6 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>
>      ret += mcgen('''
>  out_obj:
> -''')
> -    if variants:
> -        ret += mcgen('''
> -    error_propagate(errp, err);
> -    err = NULL;
> -    if (*obj) {
> -        visit_end_union(v, !!(*obj)->u.data, &err);
> -    }
> -''')
> -    ret += mcgen('''
>      error_propagate(errp, err);
>      err = NULL;
>      visit_end_struct(v, &err);

This one's a bit harder to see, but it actually matches, too.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces Eric Blake
@ 2016-01-27 17:20   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-27 17:20 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Michael S. Tsirkin, Alexander Graf, qemu-devel,
	open list:sPAPR, marcandre.lureau, Paolo Bonzini,
	Luiz Capitulino, Andreas Färber, David Gibson

Eric Blake <eblake@redhat.com> writes:

> As mentioned in previous patches, we want to call visit_end_struct()
> functions unconditionally, so that visitors can release resources
> tied up since the matching visit_start_struct() without also having
> to worry about error priority if more than one error occurs.
>
> Even though error_propagate() can be safely used to ignore a second
> error during cleanup caused by a first error, it is simpler if the
> cleanup cannot set an error, and we instead split the task of
> checking that an input visitor has no unvisited input as a new
> function visit_check_struct(), called only if all prior steps are
> successful.
>
> Generated code has diffs resembling:
>
> |@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v,
> |         goto out_obj;
> |     }
> |     visit_type_ACPIOSTInfo_fields(v, obj, &err);
> |+    if (err) {
> |+        goto out_obj;
> |+    }
> |+    visit_check_struct(v, &err);
> | out_obj:
> |-    error_propagate(errp, err);
> |-    err = NULL;
> |-    visit_end_struct(v, &err);
> |+    visit_end_struct(v);
> | out:

Basically, split visit_end_struct() into a part that runs only when
there have been no errors and a part that runs as before, i.e. when
visit_start_struct() succeeded, even when there have been errors since.
This second part cannot fail, which simplifies error handling a bit.

To review: skipping the first part when there have been errors is safe.

>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: rebase to earlier changes, drop Marc-Andre's R-b
> v8: rebase to 'name' motion
> v7: rebase to earlier changes
> v6: new patch, revised version of RFC based on discussion of v5 7/46
> ---
>  hmp.c                          |  7 ++++---
>  hw/ppc/spapr_drc.c             |  3 ++-
>  hw/virtio/virtio-balloon.c     | 12 ++++++------
>  include/qapi/visitor-impl.h    |  4 +++-
>  include/qapi/visitor.h         | 13 ++++++++++---
>  qapi/opts-visitor.c            | 17 +++++++++++++++--
>  qapi/qapi-dealloc-visitor.c    |  2 +-
>  qapi/qapi-visit-core.c         | 11 +++++++++--
>  qapi/qmp-input-visitor.c       | 34 +++++++++++++++++++---------------
>  qapi/qmp-output-visitor.c      |  2 +-
>  qom/object.c                   |  5 ++---
>  scripts/qapi-event.py          |  3 ++-
>  scripts/qapi-visit.py          |  9 ++++-----
>  tests/test-qmp-input-visitor.c |  3 ++-
>  vl.c                           | 11 +++++------
>  15 files changed, 85 insertions(+), 51 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index a4b74df..245617d 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1687,13 +1687,14 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
>      }
>
>      object_add(type, id, pdict, v, &err);
> -
> -out_end:
> -    visit_end_struct(v, &err_end);
> +    visit_check_struct(v, &err_end);
>      if (!err && err_end) {
>          qmp_object_del(id, NULL);
>      }
>      error_propagate(&err, err_end);
> +
> +out_end:
> +    visit_end_struct(v);
>  out_clean:
>      opts_visitor_cleanup(ov);
>
> diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
> index 831ce23..3b27caa 100644
> --- a/hw/ppc/spapr_drc.c
> +++ b/hw/ppc/spapr_drc.c
> @@ -287,11 +287,12 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
>          case FDT_END_NODE:
>              /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
>              g_assert(fdt_depth > 0);
> -            visit_end_struct(v, &err);
> +            visit_check_struct(v, &err);
>              if (err) {
>                  error_propagate(errp, err);
>                  return;
>              }
> +            visit_end_struct(v);
>              fdt_depth--;
>              break;
>          case FDT_PROP: {

No longer calls visit_end_struct() when visit_end_struct() fails.
Contradicts the rule to always call it after visit_start_struct()
succeded.

> diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
> index 24ecd87..cefd43f 100644
> --- a/hw/virtio/virtio-balloon.c
> +++ b/hw/virtio/virtio-balloon.c
> @@ -136,15 +136,15 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
>              goto out_nested;
>          }
>      }
> +    visit_check_struct(v, &err);
>  out_nested:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>
> +    if (!err) {
> +        visit_check_struct(v, &err);
> +    }
>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 3b68b7b..248b1e5 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -28,8 +28,10 @@ struct Visitor
>       * currently visit structs). */
>      void (*start_struct)(Visitor *v, const char *name, void **obj,
>                           size_t size, Error **errp);
> +    /* May be NULL; most useful for input visitors. */
> +    void (*check_struct)(Visitor *v, Error **errp);

Well, "most useful" is certainly correct since other visitors have had
no use for it so far :)

"intended for input visitors"?

>      /* Must be provided if start_struct is present. */
> -    void (*end_struct)(Visitor *v, Error **errp);
> +    void (*end_struct)(Visitor *v);
>
>      /* May be NULL; most useful for input visitors. */
>      void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index d7a0110..e5dcde4 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -68,12 +68,19 @@ typedef struct GenericList
>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp);
>  /**
> + * Prepare for completing a struct visit.
> + * Should be called prior to visit_end_struct() if all other intermediate
> + * visit steps were successful, to allow the caller one last chance to
> + * report errors such as remaining data that was not consumed by the visit.
> + */
> +void visit_check_struct(Visitor *v, Error **errp);
> +/**
>   * Complete a struct visit started earlier.
>   * Must be called after any successful use of visit_start_struct(),
>   * even if intermediate processing was skipped due to errors, to allow
>   * the backend to release any resources.
>   */
> -void visit_end_struct(Visitor *v, Error **errp);
> +void visit_end_struct(Visitor *v);
>
>  /**
>   * Prepare to visit an implicit struct.
> @@ -96,8 +103,8 @@ void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
>   * Must be called after any successful use of visit_start_implicit_struct(),
>   * even if intermediate processing was skipped due to errors, to allow
>   * the backend to release any resources.  Unlike visit_end_struct(), this
> - * does not need to check for errors (detection of unused keys is only
> - * possible for the overall struct, not a subset).
> + * does not need a counterpart function to check for errors (detection of
> + * unused keys is only possible for the overall struct, not a subset).
>   */
>  void visit_end_implicit_struct(Visitor *v);
>
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index df312e6..b469573 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -157,13 +157,13 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>
>
>  static void
> -opts_end_struct(Visitor *v, Error **errp)
> +opts_check_struct(Visitor *v, Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
>      GHashTableIter iter;
>      GQueue *any;
>
> -    if (--ov->depth > 0) {
> +    if (ov->depth > 0) {
>          return;
>      }
>

Not fully visible in this diff: this function's only possible side
effect is setting an error.  Therefore, skipping this when there have
been errors is safe.

> @@ -175,6 +175,18 @@ opts_end_struct(Visitor *v, Error **errp)
>          first = g_queue_peek_head(any);
>          error_setg(errp, QERR_INVALID_PARAMETER, first->name);
>      }
> +}
> +
> +
> +static void
> +opts_end_struct(Visitor *v)
> +{
> +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
> +
> +    if (--ov->depth > 0) {
> +        return;
> +    }
> +
>      g_hash_table_destroy(ov->unprocessed_opts);
>      ov->unprocessed_opts = NULL;
>      if (ov->fake_id_opt) {
> @@ -506,6 +518,7 @@ opts_visitor_new(const QemuOpts *opts)
>      ov = g_malloc0(sizeof *ov);
>
>      ov->visitor.start_struct = &opts_start_struct;
> +    ov->visitor.check_struct = &opts_check_struct;
>      ov->visitor.end_struct   = &opts_end_struct;
>
>      ov->visitor.start_list = &opts_start_list;
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index 1a54864..a89e6d1 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -65,7 +65,7 @@ static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
>      qapi_dealloc_push(qov, obj);
>  }
>
> -static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
> +static void qapi_dealloc_end_struct(Visitor *v)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      void **obj = qapi_dealloc_pop(qov);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 3360cdf..9506a02 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -36,9 +36,16 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
>      v->start_struct(v, name, obj, size, errp);
>  }
>
> -void visit_end_struct(Visitor *v, Error **errp)
> +void visit_check_struct(Visitor *v, Error **errp)
>  {
> -    v->end_struct(v, errp);
> +    if (v->check_struct) {
> +        v->check_struct(v, errp);
> +    }
> +}
> +
> +void visit_end_struct(Visitor *v)
> +{
> +    v->end_struct(v);
>  }
>
>  void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 91ef945..f256d9e 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -89,8 +89,10 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
>  }
>
>
> -static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
> +static void qmp_input_check_struct(Visitor *v, Error **errp)
>  {
> +    QmpInputVisitor *qiv = to_qiv(v);
> +
>      assert(qiv->nb_stack > 0);
>
>      if (qiv->strict) {

Same argument as for opts_check_struct().

> @@ -103,6 +105,19 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
>              if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
>                  error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
>              }
> +        }
> +    }
> +}
> +
> +static void qmp_input_pop(Visitor *v)
> +{
> +    QmpInputVisitor *qiv = to_qiv(v);
> +
> +    assert(qiv->nb_stack > 0);
> +
> +    if (qiv->strict) {
> +        GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
> +        if (top_ht) {
>              g_hash_table_unref(top_ht);
>          }
>      }
> @@ -134,12 +149,6 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>      }
>  }
>
> -static void qmp_input_end_struct(Visitor *v, Error **errp)
> -{
> -    QmpInputVisitor *qiv = to_qiv(v);
> -
> -    qmp_input_pop(qiv, errp);
> -}

Order is now funny: the end_struct() method preceeds the start_struct()
method.  I guess that's okay.

>
>  static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
>                                              size_t size, Error **errp)
> @@ -193,12 +202,6 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
>      return entry;
>  }
>
> -static void qmp_input_end_list(Visitor *v)
> -{
> -    QmpInputVisitor *qiv = to_qiv(v);
> -
> -    qmp_input_pop(qiv, &error_abort);
> -}

Likewise.

>
>  static void qmp_input_get_next_type(Visitor *v, const char *name, QType *type,
>                                      bool promote_int, Error **errp)
> @@ -355,11 +358,12 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
>      v = g_malloc0(sizeof(*v));
>
>      v->visitor.start_struct = qmp_input_start_struct;
> -    v->visitor.end_struct = qmp_input_end_struct;
> +    v->visitor.check_struct = qmp_input_check_struct;
> +    v->visitor.end_struct = qmp_input_pop;
>      v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
>      v->visitor.start_list = qmp_input_start_list;
>      v->visitor.next_list = qmp_input_next_list;
> -    v->visitor.end_list = qmp_input_end_list;
> +    v->visitor.end_list = qmp_input_pop;
>      v->visitor.type_enum = input_type_enum;
>      v->visitor.type_int64 = qmp_input_type_int64;
>      v->visitor.type_uint64 = qmp_input_type_uint64;
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 2eb200d..5376948 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -116,7 +116,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
>      qmp_output_push(qov, dict);
>  }
>
> -static void qmp_output_end_struct(Visitor *v, Error **errp)
> +static void qmp_output_end_struct(Visitor *v)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
>      qmp_output_pop(qov, QTYPE_QDICT);
> diff --git a/qom/object.c b/qom/object.c
> index e5b0566..4644cbd 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -2034,10 +2034,9 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name,
>      if (err) {
>          goto out_end;
>      }
> +    visit_check_struct(v, &err);
>  out_end:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, errp);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>
> diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
> index cc55de7..dea45c4 100644
> --- a/scripts/qapi-event.py
> +++ b/scripts/qapi-event.py
> @@ -71,8 +71,9 @@ def gen_event_send(name, arg_type):
>          ret += gen_visit_fields(arg_type.members, need_cast=True,
>                                  label='out_obj')
>          ret += mcgen('''
> +    visit_check_struct(v, &err);
>  out_obj:
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>      if (err) {
>          goto out;
>      }
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 7a44c13..8039b97 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -255,8 +255,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>      visit_type_%(c_name)s_fields(v, %(cast)sobj, &err);
>  ''',
>                       c_name=type_name, cast=cast)
> -        if variants:
> -            ret += gen_err_check(label='out_obj')
> +        ret += gen_err_check(label='out_obj')

Can't immediately see why we need this change.  Care to explain?

>
>      if variants:
>          ret += mcgen('''
> @@ -293,12 +292,12 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>          abort();
>      }
>  ''')
> +        ret += gen_err_check(label='out_obj')

Yes, because we need to skip...

>
>      ret += mcgen('''
> +    visit_check_struct(v, &err);

... this one.

>  out_obj:
> -    error_propagate(errp, err);
> -    err = NULL;
> -    visit_end_struct(v, &err);
> +    visit_end_struct(v);
>  out:
>      error_propagate(errp, err);
>  }
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index 6489e4a..cd5e765 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -290,7 +290,8 @@ static void test_visitor_in_null(TestInputVisitorData *data,
>      v = visitor_input_test_init(data, "{ 'a': null }");
>      visit_start_struct(v, NULL, NULL, 0, &error_abort);
>      visit_type_null(v, "a", &error_abort);
> -    visit_end_struct(v, &error_abort);
> +    visit_check_struct(v, &error_abort);
> +    visit_end_struct(v);
>
>      /* Check that qnull reference counting is sane:
>       * 1 for global use, 1 for our qnull() use, and 1 still owned by 'v'
> diff --git a/vl.c b/vl.c
> index 22062b5..00c92b8 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2842,11 +2842,10 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
>      qdict_del(pdict, "qom-type");
>      visit_type_str(v, "qom-type", &type, &err);
>      if (err) {
> -        goto out;
> +        goto out_end;

Before: visit_end_struct() not called.

After: visit_end_struct() called.

Shouldn't this be fixed in PATCH 05?

>      }
>      if (!type_predicate(type)) {
> -        visit_end_struct(v, NULL);
> -        goto out;
> +        goto out_end;
>      }
>
>      qdict_del(pdict, "id");
> @@ -2856,14 +2855,14 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
>      }
>
>      object_add(type, id, pdict, v, &err);
> -
> -out_end:
> -    visit_end_struct(v, &err_end);
> +    visit_check_struct(v, &err_end);
>      if (!err && err_end) {
>          qmp_object_del(id, NULL);
>      }
>      error_propagate(&err, err_end);
>
> +out_end:
> +    visit_end_struct(v);
>  out:
>      opts_visitor_cleanup(ov);

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members
  2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
@ 2016-01-27 21:48       ` Eric Blake
  0 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-27 21:48 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel; +Cc: marcandre.lureau, mdroth

[-- Attachment #1: Type: text/plain, Size: 1887 bytes --]

On 01/27/2016 06:54 AM, Markus Armbruster wrote:
> For a simple union SU, gen_visit_union() generates a visit of its
> single tag member, like this:
> 
>     visit_type_SUKind(v, "type", &(*obj)->type, &err);
> 
> For a flat union FU with base B, it generates a visit of its base
> fields:
> 
>     visit_type_B_fields(v, (B **)obj, &err);
> 
> Instead, we can simply visit the common members using the same fields
> visit function we use for structs, generated with
> gen_visit_struct_fields().  This function visits the base if any, then
> the local members.

I like how you've split up my gen_struct/gen_union merge into a few more
steps. I'll be incorporating your patch into my v10 spin.

> 
> For a simple union SU, visit_type_SU_fields() contains exactly the old
> tag member visit, because there is no base, and the tag member is the
> only member.  For instance, the code generated for
> qapi-schema-test.json's UserDefNativeListUnion changes like this:
> 
>     @@ -740,6 +766,19 @@ out:
> 	 error_propagate(errp, err);
>      }
> 
>     +static void visit_type_UserDefNativeListUnion_fields(Visitor *v, UserDefNativeListUnion **obj, Error **errp)

I may try to rewrite the commit to pick a shorter example, though :)

> 
> As you see, the generated code grows a bit, but in practice, it's lost
> in the noise: qapi-schema.json's qapi-visit.c gains roughly 1%.
> 
> This simplification became possible with commit 441cbac "qapi-visit:
> Convert to QAPISchemaVisitor, fixing bugs".  It's a step towards
> unifying gen_struct() and gen_union().
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi-visit.py | 27 ++++-----------------------
>  1 file changed, 4 insertions(+), 23 deletions(-)
> 

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list()
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list() Eric Blake
@ 2016-01-28 13:37   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-28 13:37 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Alexander Graf, qemu-devel, open list:sPAPR,
	marcandre.lureau, David Gibson

Eric Blake <eblake@redhat.com> writes:

> We have two uses of list visits in the entire code base: one in
> spapr_drc (which completely avoids visit_next_list(), feeding in
> integers from a different source than uint8List), and one in
> qapi-visit.py (that is, all other list visitors are generated
> in qapi-visit.c, and share the same paradigm based on a qapi
> FooList type).  What's more, the semantics of the list visit are
> somewhat baroque, with the following pseudocode when FooList is
> used:
>
> start()
> prev = head
> while (cur = next(prev)) {
>     visit(cur)

Actually, we pass &cur->value to the element visit.

>     prev = &cur
> }
>
> Note that these semantics (advance before visit) requires that
> the first call to next() return the list head, while all other
> calls return the next element of the list; that is, every visitor
> implementation is required to track extra state to decide whether
> to return the input as-is, or to advance.  It also requires an
> argument of 'GenericList **' to next(), solely because the first
> iteration might need to modify the caller's GenericList head, so
> that all other calls have to do a layer of dereferencing.
>
> We can greatly simplify things by hoisting the special case
> into the start() routine, and flipping the order in the loop
> to visit before advance:
>
> start(head)
> element = *head
> while (element) {
>     visit(element)
>     element = next(element)
> }

@element isn't a list element, it's a list node.  Suggest

  start(head)
  tail = *head
  while (tail) {
      visit(&tail->value)
      tail = next(tail)
  }

Of course, this pseudo-code just screams to be a for-loop instead:

  for (tail = *head; tail; tail = next(tail)) ...

May or may not be an improvement of the real code.  The pseudo-code
should follow the real code.

> With the simpler semantics, visitors have less state to track,
> the argument to next() is reduced to 'GenericList *', and it
> also becomes obvious whether an input visitor is allocating a
> FooList during visit_start_list() (rather than the old way of
> not knowing if an allocation happened until the first
> visit_next_list()).
>
> The signature of visit_start_list() is chosen to match
> visit_start_struct(), with the new parameter after 'name'.
>
> The spapr_drc case requires that visit_start_list() has to pay
> attention to whether visit_next_list() will even be used to
> visit a FooList qapi struct; this is done by passing NULL for
> list, similarly to how NULL is passed to visit_start_struct()
> when a qapi type is not used in those visits.  It was easy to
> provide these semantics for qmp-output and dealloc visitors,
> and a bit harder for qmp-input (it required hoisting the
> advance of the current qlist entry out of qmp_input_next_list()
> into qmp_input_get_object()).  But it turned out that the
> string and opts visitors munge enough state during
> visit_next_list() to make those conversions simpler if they
> require a GenericList visit for now; an assertion will remind
> us to adjust things if we need the semantics in the future.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> ---
> v9: no change
> v8: consistent parameter order, fix qmp_input_get_next_type() to not
> skip list entries
> v7: new patch
> ---
>  hw/ppc/spapr_drc.c           |  2 +-
>  include/qapi/visitor-impl.h  |  5 ++--
>  include/qapi/visitor.h       | 45 +++++++++++++++++--------------
>  qapi/opts-visitor.c          | 32 +++++++++-------------
>  qapi/qapi-dealloc-visitor.c  | 29 +++++---------------
>  qapi/qapi-visit-core.c       |  7 ++---
>  qapi/qmp-input-visitor.c     | 64 +++++++++++++++++++++-----------------------
>  qapi/qmp-output-visitor.c    | 21 +++------------
>  qapi/string-input-visitor.c  | 34 +++++++++++------------
>  qapi/string-output-visitor.c | 36 ++++++++-----------------
>  scripts/qapi-visit.py        | 21 ++++++++-------
>  11 files changed, 126 insertions(+), 170 deletions(-)

Diffstat suggests it's indeed a simplification.

>
> diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
> index 3b27caa..41f2da0 100644
> --- a/hw/ppc/spapr_drc.c
> +++ b/hw/ppc/spapr_drc.c
> @@ -299,7 +299,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
>              int i;
>              prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
>              name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
> -            visit_start_list(v, name, &err);
> +            visit_start_list(v, name, NULL, &err);
>              if (err) {
>                  error_propagate(errp, err);
>                  return;
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 248b1e5..acbe7d6 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -40,9 +40,10 @@ struct Visitor
>      void (*end_implicit_struct)(Visitor *v);
>
>      /* Must be set */
> -    void (*start_list)(Visitor *v, const char *name, Error **errp);
> +    void (*start_list)(Visitor *v, const char *name, GenericList **list,
> +                       Error **errp);
>      /* Must be set */
> -    GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
> +    GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);

You rename the parameter here, but not in the implementations.

The parameter isn't a list element, it's a list node.  If we want to
rename it, what about tail?

>      /* Must be set */
>      void (*end_list)(Visitor *v);
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index e5dcde4..4638863 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -1,6 +1,7 @@
>  /*
>   * Core Definitions for QAPI Visitor Classes
>   *
> + * Copyright (C) 2015 Red Hat, Inc.

It's 2016 now.  I'd make it 2012-2016.

>   * Copyright IBM, Corp. 2011
>   *
>   * Authors:
> @@ -111,32 +112,36 @@ void visit_end_implicit_struct(Visitor *v);
>  /**
>   * Prepare to visit a list tied to an object key @name.
>   * @name will be NULL if this is visited as part of another list.
> - * After calling this, the elements must be collected until
> - * visit_next_list() returns NULL, then visit_end_list() must be
> - * used to complete the visit.
> - */
> -void visit_start_list(Visitor *v, const char *name, Error **errp);
> -/**
> - * Iterate over a GenericList during a list visit.
> - * @list must not be NULL; on the first call, @list contains the
> - * address of the list head, and on subsequent calls *@list must be
> - * the previously returned value.  Must be called in a loop until a
> - * NULL return or error occurs; for each non-NULL return, the caller
> - * must then call the appropriate visit_type_*() for the element type
> - * of the list, with that function's name parameter set to NULL.
> + * Input visitors malloc a qapi List struct into *@list,

QAPI

What's a QAPI list struct?  I guess you mean GenericList.

>                                                           or set it to
> + * NULL if there are no elements in the list;

So start_list() now needs to know whether the list is empty.

visit_start_struct() has an "if @obj is not NULL" clause here.  Do we
need the equivalent "if @list is not NULL"?  Ah, you do that further
down!  Can we structure the two comments the same way?

>                                                and output visitors
> + * expect *@list to point to the start of the list, if any.

Perhaps "to the first list node, if any", and give GenericList a better
comment in PATCH 21:

    /*
     * Generic list node
     * Any generated QAPI FOOList struct pointer can be safely cast to
     * GenericList * and dereferenced.
     */

Of course, this actually assumes uniform pointer representation, which
is not guaranteed by the standard.

Should we mention output visitors don't change *@list?  Same for
visit_start_struct(), by the way.

What about the dealloc visitor?

>                                                               On
> + * return, if *@list is non-NULL, the caller should enter a loop
> + * visiting the current element, then using visit_next_list() to
> + * advance to the next element, until that returns NULL; then
> + * visit_end_list() must be used to complete the visit.

For visit_start_struct() this part reads:

                                                             The
 * caller then makes a series of visit calls for each key expected in
 * the object, where those visits set their respective obj parameter
 * to the address of a member of the qapi struct, and follows
 * everything by a call to visit_end_struct() to clean up resources.

Following that pattern here, I get:

   The caller then visits the list elements in turn, where those visits
   get passed the address of the list element within the QAPI list node.
   The caller normally uses visit_next_list() to step through the list.
   When done, it must call visit_end_list() to clean up.

>   *
> - * Note that for some visitors (qapi-dealloc and qmp-output), when a
> - * qapi GenericList linked list is not being used (comparable to when
> - * a NULL obj is used for visit_start_struct()), it is acceptable to
> - * bypass the use of visit_next_list() and just directly call the
> - * appropriate visit_type_*() for each element in between the
> - * visit_start_list() and visit_end_list() calls.
> + * If supported by a visitor, @list can be NULL to indicate that there

by the visitor

> + * is no qapi List struct, and that the upcoming visit calls are
> + * parsing input to or creating output from some other representation;
> + * in this case, visit_next_list() will not be needed, but
> + * visit_end_list() is still mandatory.

You first explain normal usage, then add this paragraph to explain
special usage.  I like that better than the visit_start_struct()
comment, where the special usage comes earlier.

>   *
>   * FIXME: For input visitors, *@list can be assigned here even if
>   * later visits will fail; this can lead to memory leaks if clients
>   * aren't careful.
>   */
> -GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
> +void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +                      Error **errp);
> +/**
> + * Iterate over a GenericList during a list visit.
> + * Before calling this function, the caller should use the appropriate
> + * visit_type_FOO() for the current list element at @element->value, and
> + * check for errors. @element must not be NULL; on the first iteration,
> + * it should be the value in *list after visit_start_list(); on other
> + * calls it should be the previous return value.  This function
> + * returns NULL once there are no further list elements.
> + */

I feel if we get visit_start_list()'s comment right, then this one can
concentrate on what this function does, and leave intended usage of the
start/next/end team to visit_start_list()'s comment.

> +GenericList *visit_next_list(Visitor *v, GenericList *element, Error **errp);
>  /**
>   * Complete the list started earlier.
>   * Must be called after any successful use of visit_start_list(),
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index b469573..c5a7396 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -21,9 +21,8 @@
>  enum ListMode
>  {
>      LM_NONE,             /* not traversing a list of repeated options */
> -    LM_STARTED,          /* opts_start_list() succeeded */
>
> -    LM_IN_PROGRESS,      /* opts_next_list() has been called.
> +    LM_IN_PROGRESS,      /* opts_next_list() ready to be called.
>                            *
>                            * Generating the next list link will consume the most
>                            * recently parsed QemuOpt instance of the repeated
> @@ -212,35 +211,32 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>
>
>  static void
> -opts_start_list(Visitor *v, const char *name, Error **errp)
> +opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)

I'd break this line before Error.

>  {
>      OptsVisitor *ov = to_ov(v);
>
>      /* we can't traverse a list in a list */
>      assert(ov->list_mode == LM_NONE);
> +    /* we don't support visits without GenericList, yet */
> +    assert(list);

Aha, this is why you wrote "If supported by a visitor".  The visitors
better document what they support then.

>      ov->repeated_opts = lookup_distinct(ov, name, errp);
> -    if (ov->repeated_opts != NULL) {
> -        ov->list_mode = LM_STARTED;
> +    if (ov->repeated_opts) {
> +        ov->list_mode = LM_IN_PROGRESS;
> +        *list = g_new0(GenericList, 1);
> +    } else {
> +        *list = NULL;
>      }
>  }
>
>
>  static GenericList *
> -opts_next_list(Visitor *v, GenericList **list, Error **errp)
> +opts_next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
> -    GenericList **link;
>
>      switch (ov->list_mode) {
> -    case LM_STARTED:
> -        ov->list_mode = LM_IN_PROGRESS;
> -        link = list;
> -        break;
> -
>      case LM_SIGNED_INTERVAL:
>      case LM_UNSIGNED_INTERVAL:
> -        link = &(*list)->next;
> -
>          if (ov->list_mode == LM_SIGNED_INTERVAL) {
>              if (ov->range_next.s < ov->range_limit.s) {
>                  ++ov->range_next.s;
> @@ -261,7 +257,6 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>              g_hash_table_remove(ov->unprocessed_opts, opt->name);
>              return NULL;
>          }
> -        link = &(*list)->next;
>          break;
>      }
>
> @@ -269,8 +264,8 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>          abort();
>      }
>
> -    *link = g_malloc0(sizeof **link);
> -    return *link;
> +    list->next = g_new0(GenericList, 1);
> +    return list->next;
>  }
>
>
> @@ -279,8 +274,7 @@ opts_end_list(Visitor *v)
>  {
>      OptsVisitor *ov = to_ov(v);
>
> -    assert(ov->list_mode == LM_STARTED ||
> -           ov->list_mode == LM_IN_PROGRESS ||
> +    assert(ov->list_mode == LM_IN_PROGRESS ||
>             ov->list_mode == LM_SIGNED_INTERVAL ||
>             ov->list_mode == LM_UNSIGNED_INTERVAL);
>      ov->repeated_opts = NULL;

Only slight simplification for this visitor: we lose LM_STARTED.

> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index a89e6d1..839049e 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -20,7 +20,6 @@
>  typedef struct StackEntry
>  {
>      void *value;
> -    bool is_list_head;
>      QTAILQ_ENTRY(StackEntry) node;
>  } StackEntry;
>
> @@ -41,10 +40,6 @@ static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)
>
>      e->value = value;
>
> -    /* see if we're just pushing a list head tracker */
> -    if (value == NULL) {
> -        e->is_list_head = true;
> -    }
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> @@ -92,31 +87,19 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v)
>      }
>  }
>
> -static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp)
> +static void qapi_dealloc_start_list(Visitor *v, const char *name,
> +                                    GenericList **list, Error **errp)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      qapi_dealloc_push(qov, NULL);

Do we still need to push/pop for a list?

If yes, can we push list instead of NULL?  Pointers always become more
complicated when they can be null...

>  }
>
> -static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
> +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
>                                             Error **errp)
>  {
> -    GenericList *list = *listp;
> -    QapiDeallocVisitor *qov = to_qov(v);
> -    StackEntry *e = QTAILQ_FIRST(&qov->stack);
> -
> -    if (e && e->is_list_head) {
> -        e->is_list_head = false;
> -        return list;
> -    }
> -
> -    if (list) {
> -        list = list->next;
> -        g_free(*listp);
> -        return list;
> -    }
> -
> -    return NULL;
> +    GenericList *next = list->next;
> +    g_free(list);
> +    return next;
>  }

Okay, this is actually a more worthwhile simplification.

>
>  static void qapi_dealloc_end_list(Visitor *v)
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 9506a02..f391a70 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -67,12 +67,13 @@ void visit_end_implicit_struct(Visitor *v)
>      }
>  }
>
> -void visit_start_list(Visitor *v, const char *name, Error **errp)
> +void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +                      Error **errp)
>  {
> -    v->start_list(v, name, errp);
> +    v->start_list(v, name, list, errp);
>  }
>
> -GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
> +GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      assert(list);
>      return v->next_list(v, list, errp);
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index f256d9e..82f9333 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -44,16 +44,20 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
>                                       const char *name,
>                                       bool consume)
>  {
> -    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
> +    StackObject *so = &qiv->stack[qiv->nb_stack - 1];
> +    QObject *qobj = so->obj;
>
>      if (qobj) {
>          if (name && qobject_type(qobj) == QTYPE_QDICT) {
> -            if (qiv->stack[qiv->nb_stack - 1].h && consume) {
> -                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
> +            if (so->h && consume) {
> +                g_hash_table_remove(so->h, name);
> +            }
> +            qobj = qdict_get(qobject_to_qdict(qobj), name);
> +        } else if (so->entry) {
> +            qobj = qlist_entry_obj(so->entry);
> +            if (consume) {
> +                so->entry = qlist_next(so->entry);
>              }
> -            return qdict_get(qobject_to_qdict(qobj), name);
> -        } else if (qiv->stack[qiv->nb_stack - 1].entry) {
> -            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
>          }
>      }
>

Much easier to read if split into two steps:

1. Capture the pointer to the top of the stack in a variable

@@ -44,16 +44,17 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
                                      const char *name,
                                      bool consume)
 {
-    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
+    StackObject *so = &qiv->stack[qiv->nb_stack - 1];
+    QObject *qobj = so->obj;
 
     if (qobj) {
         if (name && qobject_type(qobj) == QTYPE_QDICT) {
-            if (qiv->stack[qiv->nb_stack - 1].h && consume) {
-                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name);
+            if (so->h && consume) {
+                g_hash_table_remove(so->h, name);
             }
             return qdict_get(qobject_to_qdict(qobj), name);
-        } else if (qiv->stack[qiv->nb_stack - 1].entry) {
-            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry);
+        } else if (so->entry) {
+            return qlist_entry_obj(so->entry);
         }
     }
 
2. Make the actual change

@@ -52,9 +52,12 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
             if (so->h && consume) {
                 g_hash_table_remove(so->h, name);
             }
-            return qdict_get(qobject_to_qdict(qobj), name);
+            qobj = qdict_get(qobject_to_qdict(qobj), name);
         } else if (so->entry) {
-            return qlist_entry_obj(so->entry);
+            qobj = qlist_entry_obj(so->entry);
+            if (consume) {
+                so->entry = qlist_next(so->entry);
+            }
         }
     }
 
     return qobj;
 
I'd call the new variable tos (top of stack) instead of so.  Needs a
matching rename in qmp_input_next_list().

Note that @consume is true unless we're called from
qmp_input_get_next_type().  What is it supposed to do?  Can it be true
in the place where you add a use?

Since we're cleaning up the function anyway in step 1, I'd be tempted to
reduce its nesting:

    if (!qobj) {
        return NULL;
    } else if (name && qobject_type(qobj) == QTYPE_QDICT) {
        if (tos->h && consume) {
            g_hash_table_remove(tos->h, name);
        }
        return qdict_get(qobject_to_qdict(qobj), name);
    } else if (tos->entry) {
        return qlist_entry_obj(tos->entry);
    } else {
        return qobj;
    }

Immediately begs the question what the four cases mean.  Unobvious
enough to justify comments, I think.

What's the stack's contents?  You cleaned that up and documented it in
qmp-output-visitor.c.  Same treatment here?

> @@ -66,7 +70,8 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque)
>      g_hash_table_insert(h, (gpointer) key, NULL);
>  }
>
> -static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
> +static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
> +                           const QListEntry *entry, Error **errp)
>  {
>      GHashTable *h;
>
       if (qiv->nb_stack >= QIV_STACK_SIZE) {
           error_setg(errp, "An internal buffer overran");
           return;
       }

Aside: this is stupid, realloc() exists.  It's less stupid than the
qmp-output-visitor.c's tail queue, though.

> @@ -76,7 +81,7 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
>      }
>
>      qiv->stack[qiv->nb_stack].obj = obj;
> -    qiv->stack[qiv->nb_stack].entry = NULL;
> +    qiv->stack[qiv->nb_stack].entry = entry;
>      qiv->stack[qiv->nb_stack].h = NULL;
>
>      if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
> @@ -138,7 +143,7 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>          return;
>      }
>
> -    qmp_input_push(qiv, qobj, &err);
> +    qmp_input_push(qiv, qobj, NULL, &err);
>      if (err) {
>          error_propagate(errp, err);
>          return;

The stack entry for a struct being visited has obj = its source QDict,
entry = NULL.

> @@ -158,10 +163,12 @@ static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
>      }
>  }
>
> -static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
> +static void qmp_input_start_list(Visitor *v, const char *name,
> +                                 GenericList **list, Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
>      QObject *qobj = qmp_input_get_object(qiv, name, true);
> +    const QListEntry *entry;
>
>      if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
> @@ -169,37 +176,28 @@ static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
>          return;
>      }
>
> -    qmp_input_push(qiv, qobj, errp);
> +    entry = qlist_first(qobject_to_qlist(qobj));
> +    qmp_input_push(qiv, qobj, entry, errp);

The stack entry for a list being visited has obj = its source QList,
entry = NULL.

> +    if (list) {
> +        if (entry) {
> +            *list = g_new0(GenericList, 1);
> +        } else {
> +            *list = NULL;
> +        }
> +    }
>  }
>
> -static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
> +static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
>                                          Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> -    GenericList *entry;
>      StackObject *so = &qiv->stack[qiv->nb_stack - 1];
> -    bool first;
>
> -    if (so->entry == NULL) {
> -        so->entry = qlist_first(qobject_to_qlist(so->obj));
> -        first = true;
> -    } else {
> -        so->entry = qlist_next(so->entry);
> -        first = false;
> -    }
> -
> -    if (so->entry == NULL) {
> +    if (!so->entry) {
>          return NULL;
>      }
> -
> -    entry = g_malloc0(sizeof(*entry));
> -    if (first) {
> -        *list = entry;
> -    } else {
> -        (*list)->next = entry;
> -    }
> -
> -    return entry;
> +    list->next = g_new0(GenericList, 1);
> +    return list->next;
>  }
>
>
> @@ -375,7 +373,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
>      v->visitor.optional = qmp_input_optional;
>      v->visitor.get_next_type = qmp_input_get_next_type;
>
> -    qmp_input_push(v, obj, NULL);
> +    qmp_input_push(v, obj, NULL, NULL);

The stack entry for the root value being visited has obj = its source
QObject, entry = NULL.

>      qobject_incref(obj);
>
>      return v;
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 5376948..913f378 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -21,7 +21,6 @@
>  typedef struct QStackEntry
>  {
>      QObject *value;
> -    bool is_list_head;
>      QTAILQ_ENTRY(QStackEntry) node;
>  } QStackEntry;
>
> @@ -51,9 +50,6 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
>      assert(qov->root);
>      assert(value);
>      e->value = value;
> -    if (qobject_type(e->value) == QTYPE_QLIST) {
> -        e->is_list_head = true;
> -    }
>      QTAILQ_INSERT_HEAD(&qov->stack, e, node);
>  }
>
> @@ -122,7 +118,8 @@ static void qmp_output_end_struct(Visitor *v)
>      qmp_output_pop(qov, QTYPE_QDICT);
>  }
>
> -static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
> +static void qmp_output_start_list(Visitor *v, const char *name,
> +                                  GenericList **listp, Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
>      QList *list = qlist_new();
> @@ -131,20 +128,10 @@ static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
>      qmp_output_push(qov, list);
>  }
>
> -static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
> +static GenericList *qmp_output_next_list(Visitor *v, GenericList *list,
>                                           Error **errp)
>  {
> -    GenericList *list = *listp;
> -    QmpOutputVisitor *qov = to_qov(v);
> -    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> -
> -    assert(e);
> -    if (e->is_list_head) {
> -        e->is_list_head = false;
> -        return list;
> -    }
> -
> -    return list ? list->next : NULL;
> +    return list->next;
>  }
>
>  static void qmp_output_end_list(Visitor *v)

A simple one, for a change.

> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index 610c233..582a62a 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -23,8 +23,6 @@ struct StringInputVisitor
>  {
>      Visitor visitor;
>
> -    bool head;
> -
>      GList *ranges;
>      GList *cur_range;
>      int64_t cur;
> @@ -123,11 +121,19 @@ error:
>  }
>
>  static void
> -start_list(Visitor *v, const char *name, Error **errp)
> +start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>  {
>      StringInputVisitor *siv = to_siv(v);
> +    Error *err = NULL;
>
> -    parse_str(siv, errp);
> +    /* We don't support visits without a GenericList, yet */
> +    assert(list);
> +
> +    parse_str(siv, &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }

Should you set *@list = NULL on error?

Does this handle parse_str() error correctly before your patch?

>
>      siv->cur_range = g_list_first(siv->ranges);
>      if (siv->cur_range) {
> @@ -135,14 +141,16 @@ start_list(Visitor *v, const char *name, Error **errp)
>          if (r) {
>              siv->cur = r->begin;
>          }
> +        *list = g_new0(GenericList, 1);
> +    } else {
> +        *list = NULL;
>      }
>  }

If it does, then this must be the reason you have to bail out on error.

>
>  static GenericList *
> -next_list(Visitor *v, GenericList **list, Error **errp)
> +next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      StringInputVisitor *siv = to_siv(v);
> -    GenericList **link;
>      Range *r;
>
>      if (!siv->ranges || !siv->cur_range) {
> @@ -166,22 +174,13 @@ next_list(Visitor *v, GenericList **list, Error **errp)
>          siv->cur = r->begin;
>      }
>
> -    if (siv->head) {
> -        link = list;
> -        siv->head = false;
> -    } else {
> -        link = &(*list)->next;
> -    }
> -
> -    *link = g_malloc0(sizeof **link);
> -    return *link;
> +    list->next = g_new0(GenericList, 1);
> +    return list->next;
>  }
>
>  static void
>  end_list(Visitor *v)
>  {
> -    StringInputVisitor *siv = to_siv(v);
> -    siv->head = true;
>  }
>
>  static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
> @@ -361,6 +360,5 @@ StringInputVisitor *string_input_visitor_new(const char *str)
>      v->visitor.optional = parse_optional;
>
>      v->string = str;
> -    v->head = true;
>      return v;
>  }
> diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
> index fd917a4..7209d80 100644
> --- a/qapi/string-output-visitor.c
> +++ b/qapi/string-output-visitor.c
> @@ -57,7 +57,6 @@ struct StringOutputVisitor
>      Visitor visitor;
>      bool human;
>      GString *string;
> -    bool head;
>      ListMode list_mode;
>      union {
>          int64_t s;
> @@ -265,40 +264,29 @@ static void print_type_number(Visitor *v, const char *name, double *obj,
>  }
>
>  static void
> -start_list(Visitor *v, const char *name, Error **errp)
> +start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>  {
>      StringOutputVisitor *sov = to_sov(v);
>
>      /* we can't traverse a list in a list */
>      assert(sov->list_mode == LM_NONE);
> -    sov->list_mode = LM_STARTED;
> -    sov->head = true;
> +    /* We don't support visits without a GenericList, yet */
> +    assert(list);
> +    /* List handling is only needed if there are at least two elements */
> +    if (*list && (*list)->next) {
> +        sov->list_mode = LM_STARTED;
> +    }

Contradicts the comment next to LM_STARTED: /* start_list() succeeded */

Why do you need to stay in state LM_NONE for shorter lists now?


>  }
>
>  static GenericList *
> -next_list(Visitor *v, GenericList **list, Error **errp)
> +next_list(Visitor *v, GenericList *list, Error **errp)
>  {
>      StringOutputVisitor *sov = to_sov(v);
> -    GenericList *ret = NULL;
> -    if (*list) {
> -        if (sov->head) {
> -            ret = *list;
> -        } else {
> -            ret = (*list)->next;
> -        }
> +    GenericList *ret = list->next;
>
> -        if (sov->head) {
> -            if (ret && ret->next == NULL) {
> -                sov->list_mode = LM_NONE;
> -            }
> -            sov->head = false;
> -        } else {
> -            if (ret && ret->next == NULL) {
> -                sov->list_mode = LM_END;
> -            }
> -        }
> +    if (ret && !ret->next) {
> +        sov->list_mode = LM_END;
>      }
> -
>      return ret;
>  }

What does state LM_END mean?  It has no comment...

>
> @@ -312,8 +300,6 @@ end_list(Visitor *v)
>             sov->list_mode == LM_NONE ||
>             sov->list_mode == LM_IN_PROGRESS);
>      sov->list_mode = LM_NONE;
> -    sov->head = true;
> -
>  }
>
>  char *string_output_get_string(StringOutputVisitor *sov)

Unless I'm blind, no next_list() implementation sets an error.  Drop
parameter errp and simplify?

> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 8039b97..6016734 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -125,20 +125,23 @@ def gen_visit_list(name, element_type):
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
>  {
>      Error *err = NULL;
> -    GenericList *i, **prev;
> +    %(c_name)s *elt;

This isn't an element, it's a list node.  I'd call it tail.

Sure changing its type from GenericList to the specific one saves casts?

>
> -    visit_start_list(v, name, &err);
> +    visit_start_list(v, name, (GenericList **)obj, &err);
>      if (err) {
>          goto out;
>      }
> -
> -    for (prev = (GenericList **)obj;
> -         !err && (i = visit_next_list(v, prev, &err)) != NULL;
> -         prev = &i) {
> -        %(c_name)s *native_i = (%(c_name)s *)i;
> -        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
> +    elt = *obj;
> +    while (elt) {
> +        visit_type_%(c_elt_type)s(v, NULL, &elt->value, &err);
> +        if (err) {
> +            break;
> +        }
> +        elt = (%(c_name)s *)visit_next_list(v, (GenericList *)elt, &err);
> +        if (err) {
> +            break;
> +        }
>      }

With a simplified next_list(), this could be a nice for-loop:

       for (tail = *obj; tail; tail = visit_next_list(v, tail, &err)) ...

> -
>      visit_end_list(v);
>  out:
>      error_propagate(errp, err);

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
@ 2016-01-28 15:24   ` Markus Armbruster
  2016-01-28 17:05     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-28 15:24 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Returning a partial object on error is an invitation for a careless
> caller to leak memory.  As no one outside the testsuite was actually
> relying on these semantics, it is cleaner to just document and
> guarantee that ALL pointer-based visit_type_FOO() functions always
> leave a safe value in *obj during an input visitor (either the new
> object on success, or NULL if an error is encountered).
>
> Since input visitors have blind assignment semantics, we have to
> track the result of whether an assignment is made all the way down
> to each visitor callback implementation, to avoid making decisions
> based on potentially uninitialized storage.

I'm not sure I get this paragraph.  What's "blind assignment semantics"?

> Note that we still leave *obj unchanged after a scalar-based
> visit_type_FOO(); I did not feel like auditing all uses of
> visit_type_Enum() to see if the callers would tolerate a specific
> sentinel value (not to mention having to decide whether it would
> be better to use 0 or ENUM__MAX as that sentinel).

The assigning input visitor functions (core and generated) all assign
either a pointer to a newly allocated object, or a non-pointer scalar
value.

Possible behaviors on error:

(0) What we have now: assign something that must be cleaned up with the
    dealloc visitor if it's a pointer, but is otherwise useless

    CON: callers have to clean up
    CON: exposes careless callers to useless values

(1) Don't assign anything

    PRO: consistent
    CON: exposes careless callers to uninitialized values

(2) Assign zero bits

    PRO: consistent
    CON: exposes careless callers to bogus zero values

(3) Assign null pointer, else don't assign anything

    CON: inconsistent
    CON: mix of (1)'s and (2)'s CON

(4) Other ideas?

Note that *obj is almost always null on entry, because we allocate
objects zero-initialized.  Only root visits can expose their caller to
uninitialized values.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: fix bug in use of errp
> v8: rebase to earlier changes
> v7: rebase to earlier changes, enhance commit message, also fix
> visit_type_str() and visit_type_any()
> v6: rebase on top of earlier doc and formatting improvements, mention
> that *obj can be uninitialized on entry to an input visitor, rework
> semantics to keep valgrind happy on uninitialized input, break some
> long lines
> ---
>  include/qapi/visitor-impl.h    |  6 ++---
>  include/qapi/visitor.h         | 53 ++++++++++++++++++++++++++++--------------
>  qapi/opts-visitor.c            | 11 ++++++---
>  qapi/qapi-dealloc-visitor.c    |  9 ++++---
>  qapi/qapi-visit-core.c         | 45 ++++++++++++++++++++++++++++-------
>  qapi/qmp-input-visitor.c       | 18 +++++++++-----
>  qapi/qmp-output-visitor.c      |  6 +++--
>  qapi/string-input-visitor.c    |  6 +++--
>  qapi/string-output-visitor.c   |  3 ++-
>  scripts/qapi-visit.py          | 40 +++++++++++++++++++++++--------
>  tests/test-qmp-commands.c      | 13 +++++------
>  tests/test-qmp-input-strict.c  | 19 +++++++--------
>  tests/test-qmp-input-visitor.c | 10 ++------
>  13 files changed, 157 insertions(+), 82 deletions(-)
>
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index acbe7d6..8df4ba1 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -26,7 +26,7 @@ struct Visitor
>  {
>      /* Must be provided to visit structs (the string visitors do not
>       * currently visit structs). */
> -    void (*start_struct)(Visitor *v, const char *name, void **obj,
> +    bool (*start_struct)(Visitor *v, const char *name, void **obj,
>                           size_t size, Error **errp);
>      /* May be NULL; most useful for input visitors. */
>      void (*check_struct)(Visitor *v, Error **errp);
> @@ -34,13 +34,13 @@ struct Visitor
>      void (*end_struct)(Visitor *v);
>
>      /* May be NULL; most useful for input visitors. */
> -    void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
> +    bool (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
>                                    Error **errp);
>      /* May be NULL */
>      void (*end_implicit_struct)(Visitor *v);
>
>      /* Must be set */
> -    void (*start_list)(Visitor *v, const char *name, GenericList **list,
> +    bool (*start_list)(Visitor *v, const char *name, GenericList **list,
>                         Error **errp);
>      /* Must be set */
>      GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 4638863..4eae633 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -31,6 +31,27 @@
>   * visitor-impl.h.
>   */
>
> +/* All qapi types have a corresponding function with a signature
> + * roughly compatible with this:
> + *
> + * void visit_type_FOO(Visitor *v, void *obj, const char *name, Error **errp);
> + *
> + * where *@obj is itself a pointer or a scalar.  The visit functions for
> + * built-in types are declared here, while the functions for qapi-defined
> + * struct, union, enum, and list types are generated (see qapi-visit.h).
> + * Input visitors may receive an uninitialized *@obj, and guarantee a
> + * safe value is assigned (a new object on success, or NULL on failure).

This specifies the safe value: NULL.  Makes no sense for non-pointer
scalars.

May input visitors really receive uninitialized *@obj?  As far as I can
see, we routinely store a newly allocated object to *@obj on success,
and store nothing on failure.  To be able to pass this to the dealloc
visitor, *@obj must have been null initially, mustn't it?

> + * Output visitors expect *@obj to be properly initialized on entry.

What about the dealloc visitor?

> + *
> + * Additionally, all qapi structs have a generated function compatible
> + * with this:
> + *
> + * void qapi_free_FOO(void *obj);
> + *
> + * which behaves like free(), even if @obj is NULL or was only partially
> + * allocated before encountering an error.

Will partially allocated QAPI objects remain visible outside input
visitors?

> + */
> +
>  /* This struct is layout-compatible with all other *List structs
>   * created by the qapi generator. */
>  typedef struct GenericList
> @@ -62,11 +83,12 @@ typedef struct GenericList
>   * Set *@errp on failure; for example, if the input stream does not
>   * have a member @name or if the member is not an object.
>   *
> - * FIXME: For input visitors, *@obj can be assigned here even if later
> - * visits will fail; this can lead to memory leaks if clients aren't
> - * careful.
> + * Returns true if *@obj was allocated; if that happens, and an error
> + * occurs any time before the matching visit_end_struct(), then the
> + * caller (usually a visit_type_FOO() function) knows to undo the
> + * allocation before returning control further.
>   */
> -void visit_start_struct(Visitor *v, const char *name, void **obj,
> +bool visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp);

Need to see how this is used before I can judge whether I like it :)

>  /**
>   * Prepare for completing a struct visit.
> @@ -85,19 +107,15 @@ void visit_end_struct(Visitor *v);
>
>  /**
>   * Prepare to visit an implicit struct.
> - * Similar to visit_start_struct(), except that an implicit struct
> - * represents a subset of keys that are present at the same nesting level
> - * of a common object but as a separate qapi C struct, rather than a new
> - * object at a deeper nesting level.
> + * Similar to visit_start_struct(), including return semantics, except
> + * that an implicit struct represents a subset of keys that are present
> + * at the same nesting level of a common object but as a separate qapi
> + * C struct, rather than a new object at a deeper nesting level.
>   *
>   * @obj must not be NULL, since this function is only called when
>   * visiting with qapi structs.
> - *
> - * FIXME: For input visitors, *@obj can be assigned here even if later
> - * visits will fail; this can lead to memory leaks if clients aren't
> - * careful.
>   */
> -void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
> +bool visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
>                                   Error **errp);
>  /**
>   * Complete an implicit struct visit started earlier.
> @@ -126,11 +144,12 @@ void visit_end_implicit_struct(Visitor *v);
>   * in this case, visit_next_list() will not be needed, but
>   * visit_end_list() is still mandatory.
>   *
> - * FIXME: For input visitors, *@list can be assigned here even if
> - * later visits will fail; this can lead to memory leaks if clients
> - * aren't careful.
> + * Returns true if *@list was allocated; if that happens, and an error
> + * occurs any time before the matching visit_end_list(), then the
> + * caller (usually a visit_type_FOO() function) knows to undo the
> + * allocation before returning control further.
>   */
> -void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +bool visit_start_list(Visitor *v, const char *name, GenericList **list,
>                        Error **errp);
>  /**
>   * Iterate over a GenericList during a list visit.
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index c5a7396..38d1e68 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -122,18 +122,20 @@ opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
>  }
>
>
> -static void
> +static bool
>  opts_start_struct(Visitor *v, const char *name, void **obj,
>                    size_t size, Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
>      const QemuOpt *opt;
> +    bool result = false;
>
>      if (obj) {
>          *obj = g_malloc0(size > 0 ? size : 1);
> +        result = true;
>      }
>      if (ov->depth++ > 0) {
> -        return;
> +        return result;
>      }
>
>      ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
> @@ -152,6 +154,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>          ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
>          opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
>      }
> +    return result;
>  }

Stores the newly allocated object in *@obj on success, doesn't store
anything on failure.

To make cleanup possible, *@obj must be null initially.  Then the return
value is true iff *@obj is non-null on return.

>
>
> @@ -210,7 +213,7 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>  }
>
>
> -static void
> +static bool
>  opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
> @@ -223,8 +226,10 @@ opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>      if (ov->repeated_opts) {
>          ov->list_mode = LM_IN_PROGRESS;
>          *list = g_new0(GenericList, 1);
> +        return true;
>      } else {
>          *list = NULL;
> +        return false;
>      }
>  }

This one stores null on failure, unlike opts_start_struct().

Again, the return value is true iff *@list is non-null on return.

>
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index 839049e..0990199 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -53,11 +53,12 @@ static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
>      return value;
>  }
>
> -static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
> +static bool qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
>                                        size_t unused, Error **errp)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      qapi_dealloc_push(qov, obj);
> +    return false;
>  }
>

Not an input visitor, always returns false.

>  static void qapi_dealloc_end_struct(Visitor *v)
> @@ -69,13 +70,14 @@ static void qapi_dealloc_end_struct(Visitor *v)
>      }
>  }
>
> -static void qapi_dealloc_start_implicit_struct(Visitor *v,
> +static bool qapi_dealloc_start_implicit_struct(Visitor *v,
>                                                 void **obj,
>                                                 size_t size,
>                                                 Error **errp)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      qapi_dealloc_push(qov, obj);
> +    return false;
>  }
>

Likewise.

>  static void qapi_dealloc_end_implicit_struct(Visitor *v)
> @@ -87,11 +89,12 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v)
>      }
>  }
>
> -static void qapi_dealloc_start_list(Visitor *v, const char *name,
> +static bool qapi_dealloc_start_list(Visitor *v, const char *name,
>                                      GenericList **list, Error **errp)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      qapi_dealloc_push(qov, NULL);
> +    return false;
>  }
>

Likewise.

>  static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index f391a70..259e0cb 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -2,7 +2,7 @@
>   * Core Definitions for QAPI Visitor Classes
>   *
>   * Copyright IBM, Corp. 2011
> - * Copyright (C) 2015 Red Hat, Inc.
> + * Copyright (C) 2015-2016 Red Hat, Inc.
>   *
>   * Authors:
>   *  Anthony Liguori   <aliguori@us.ibm.com>
> @@ -26,14 +26,20 @@ static bool visit_is_output(Visitor *v)
>      return v->type_enum == output_type_enum;
>  }
>
> -void visit_start_struct(Visitor *v, const char *name, void **obj,
> +bool visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp)
>  {
> +    bool result;
> +
>      assert(obj ? size : !size);
>      if (obj && visit_is_output(v)) {
>          assert(*obj);
>      }
> -    v->start_struct(v, name, obj, size, errp);
> +    result = v->start_struct(v, name, obj, size, errp);
> +    if (result) {
> +        assert(obj && *obj);
> +    }

Roundabout way to write assert(!result || (obj && *obj));

Heh, you even assert one half of what I'm trying to prove.

Can we make it assert(result == (visit_is_input(v) && obj && *obj) ?

Similarly for the other functions.

> +    return result;
>  }
>
>  void visit_check_struct(Visitor *v, Error **errp)
> @@ -48,16 +54,22 @@ void visit_end_struct(Visitor *v)
>      v->end_struct(v);
>  }
>
> -void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
> +bool visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
>                                   Error **errp)
>  {
> +    bool result = false;
> +
>      assert(obj && size);
>      if (visit_is_output(v)) {
>          assert(*obj);
>      }
>      if (v->start_implicit_struct) {
> -        v->start_implicit_struct(v, obj, size, errp);
> +        result = v->start_implicit_struct(v, obj, size, errp);
>      }
> +    if (result) {
> +        assert(*obj);
> +    }
> +    return result;
>  }
>
>  void visit_end_implicit_struct(Visitor *v)
> @@ -67,10 +79,14 @@ void visit_end_implicit_struct(Visitor *v)
>      }
>  }
>
> -void visit_start_list(Visitor *v, const char *name, GenericList **list,
> +bool visit_start_list(Visitor *v, const char *name, GenericList **list,
>                        Error **errp)
>  {
> -    v->start_list(v, name, list, errp);
> +    bool result = v->start_list(v, name, list, errp);
> +    if (result) {
> +        assert(list && *list);
> +    }
> +    return result;
>  }
>
>  GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
> @@ -229,6 +245,7 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
>
>  void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>  {
> +    Error *err = NULL;
>      assert(obj);
>      /* TODO: Fix callers to not pass NULL when they mean "", so that we
>       * can enable:
> @@ -236,7 +253,11 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>          assert(*obj);
>      }
>       */
> -    v->type_str(v, name, obj, errp);
> +    v->type_str(v, name, obj, &err);
> +    if (!visit_is_output(v) && err) {
> +        *obj = NULL;

Shouldn't storing null on error be left to v->type_str(), like we do for
structs and lists?  Not least because it begs the question whether this
leaks a string stored by it.

!visit_is_output(v) covers input visitors and the dealloc visitor.

> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_type_number(Visitor *v, const char *name, double *obj,
> @@ -248,11 +269,17 @@ void visit_type_number(Visitor *v, const char *name, double *obj,
>
>  void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
>  {
> +    Error *err = NULL;
> +
>      assert(obj);
>      if (visit_is_output(v)) {
>          assert(*obj);
>      }
> -    v->type_any(v, name, obj, errp);
> +    v->type_any(v, name, obj, &err);
> +    if (!visit_is_output(v) && err) {
> +        *obj = NULL;

Likewise.

> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_type_null(Visitor *v, const char *name, Error **errp)
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 82f9333..6b4ad68 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -130,7 +130,7 @@ static void qmp_input_pop(Visitor *v)
>      qiv->nb_stack--;
>  }
>
> -static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
> +static bool qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>                                     size_t size, Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> @@ -140,30 +140,34 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>      if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "QDict");
> -        return;
> +        return false;
>      }
>
>      qmp_input_push(qiv, qobj, NULL, &err);
>      if (err) {
>          error_propagate(errp, err);
> -        return;
> +        return false;
>      }
>
>      if (obj) {
>          *obj = g_malloc0(size);
> +        return true;
>      }
> +    return false;

Stores the newly allocated object in *@obj on success, doesn't store
anything on failure.

To make cleanup possible, *@obj must be null initially.  Then the return
value is true iff *@obj is non-null on return.

Just like opts-visitor.c.

>  }
>
>
> -static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
> +static bool qmp_input_start_implicit_struct(Visitor *v, void **obj,
>                                              size_t size, Error **errp)
>  {
>      if (obj) {
>          *obj = g_malloc0(size);
> +        return true;
>      }
> +    return false;
>  }

Likewise.

>
> -static void qmp_input_start_list(Visitor *v, const char *name,
> +static bool qmp_input_start_list(Visitor *v, const char *name,
>                                   GenericList **list, Error **errp)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
> @@ -173,7 +177,7 @@ static void qmp_input_start_list(Visitor *v, const char *name,
>      if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "list");
> -        return;
> +        return false;
>      }
>
>      entry = qlist_first(qobject_to_qlist(qobj));
> @@ -181,10 +185,12 @@ static void qmp_input_start_list(Visitor *v, const char *name,
>      if (list) {
>          if (entry) {
>              *list = g_new0(GenericList, 1);
> +            return true;
>          } else {
>              *list = NULL;
>          }
>      }
> +    return false;
>  }

This one stores null on failure, unlike start_struct().

Again, the return value is true iff *@list is non-null on return.

Just like opts-visitor.c.

>
>  static GenericList *qmp_input_next_list(Visitor *v, GenericList *list,
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 913f378..ce592d2 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -102,7 +102,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
>      }
>  }
>
> -static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
> +static bool qmp_output_start_struct(Visitor *v, const char *name, void **obj,
>                                      size_t unused, Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
> @@ -110,6 +110,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
>
>      qmp_output_add(qov, name, dict);
>      qmp_output_push(qov, dict);
> +    return false;
>  }

Not an input visitor, always returns false.

>
>  static void qmp_output_end_struct(Visitor *v)
> @@ -118,7 +119,7 @@ static void qmp_output_end_struct(Visitor *v)
>      qmp_output_pop(qov, QTYPE_QDICT);
>  }
>
> -static void qmp_output_start_list(Visitor *v, const char *name,
> +static bool qmp_output_start_list(Visitor *v, const char *name,
>                                    GenericList **listp, Error **errp)
>  {
>      QmpOutputVisitor *qov = to_qov(v);
> @@ -126,6 +127,7 @@ static void qmp_output_start_list(Visitor *v, const char *name,
>
>      qmp_output_add(qov, name, list);
>      qmp_output_push(qov, list);
> +    return false;
>  }

Likewise.

>
>  static GenericList *qmp_output_next_list(Visitor *v, GenericList *list,
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index 582a62a..0e4a07c 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -120,7 +120,7 @@ error:
>      siv->ranges = NULL;
>  }
>
> -static void
> +static bool
>  start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>  {
>      StringInputVisitor *siv = to_siv(v);
> @@ -132,7 +132,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>      parse_str(siv, &err);
>      if (err) {
>          error_propagate(errp, err);
> -        return;
> +        return false;
>      }
>
>      siv->cur_range = g_list_first(siv->ranges);
> @@ -142,8 +142,10 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>              siv->cur = r->begin;
>          }
>          *list = g_new0(GenericList, 1);
> +        return true;
>      } else {
>          *list = NULL;
> +        return false;
>      }
>  }

This one stores null on failure, like the start_list() we've seen
before, and unlike the start_struct().

Again, the return value is true iff *@list is non-null on return.

>
> diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
> index 7209d80..2666802 100644
> --- a/qapi/string-output-visitor.c
> +++ b/qapi/string-output-visitor.c
> @@ -263,7 +263,7 @@ static void print_type_number(Visitor *v, const char *name, double *obj,
>      string_output_set(sov, g_strdup_printf("%f", *obj));
>  }
>
> -static void
> +static bool
>  start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>  {
>      StringOutputVisitor *sov = to_sov(v);
> @@ -276,6 +276,7 @@ start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>      if (*list && (*list)->next) {
>          sov->list_mode = LM_STARTED;
>      }
> +    return false;
>  }

Not an input visitor, always returns false.

>
>  static GenericList *
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 6016734..eebb5f7 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -58,8 +58,10 @@ def gen_visit_implicit_struct(typ):
>  static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
>  {
>      Error *err = NULL;
> +    bool allocated;
>
> -    visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
> +    allocated = visit_start_implicit_struct(v, (void **)obj,
> +                                            sizeof(%(c_type)s), &err);
>      if (err) {
>          goto out;
>      }
> @@ -69,6 +71,10 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
>      visit_type_%(c_type)s_fields(v, obj, &err);
>  out_obj:
>      visit_end_implicit_struct(v);
> +    if (allocated && err) {
> +        g_free(*obj);
> +        *obj = NULL;
> +    }
>  out:
>      error_propagate(errp, err);
>  }
> @@ -116,18 +122,15 @@ out:
>
>
>  def gen_visit_list(name, element_type):
> -    # FIXME: if *obj is NULL on entry, and the first visit_next_list()
> -    # assigns to *obj, while a later one fails, we should clean up *obj
> -    # rather than leaving it non-NULL. As currently written, the caller must
> -    # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
>      return mcgen('''
>
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
>  {
>      Error *err = NULL;
>      %(c_name)s *elt;
> +    bool allocated;
>
> -    visit_start_list(v, name, (GenericList **)obj, &err);
> +    allocated = visit_start_list(v, name, (GenericList **)obj, &err);
>      if (err) {
>          goto out;
>      }
> @@ -144,6 +147,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>      }
>      visit_end_list(v);
>  out:
> +    if (allocated && err) {
> +        qapi_free_%(c_name)s(*obj);
> +        *obj = NULL;
> +    }
>      error_propagate(errp, err);
>  }
>  ''',
> @@ -174,8 +181,10 @@ def gen_visit_alternate(name, variants):
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
>  {
>      Error *err = NULL;
> +    bool allocated;
>
> -    visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
> +    allocated = visit_start_implicit_struct(v, (void **)obj,
> +                                            sizeof(%(c_name)s), &err);
>      if (err) {
>          goto out;
>      }
> @@ -204,11 +213,15 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>      }
>  out_obj:
>      visit_end_implicit_struct(v);
> +    if (allocated && err) {
> +        qapi_free_%(c_name)s(*obj);
> +        *obj = NULL;
> +    }
>  out:
>      error_propagate(errp, err);
>  }
>  ''',
> -                 name=name)
> +                 name=name, c_name=c_name(name))
>
>      return ret
>
> @@ -236,8 +249,10 @@ def gen_visit_object(name, base, members, variants):
>  void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
>  {
>      Error *err = NULL;
> +    bool allocated;
>
> -    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
> +    allocated = visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s),
> +                                   &err);
>      if (err) {
>          goto out;
>      }
> @@ -301,10 +316,15 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>      visit_check_struct(v, &err);
>  out_obj:
>      visit_end_struct(v);
> +    if (allocated && err) {
> +        qapi_free_%(c_name)s(*obj);
> +        *obj = NULL;
> +    }
>  out:
>      error_propagate(errp, err);
>  }
> -''')
> +''',
> +                 c_name=c_name(name))
>
>      return ret
>

Hmm.

This grows qapi-visit.c by 14% for me.

Can we do the deallocation in the visitor core somehow?  We'd have to
pass "how to deallocate" information: the appropriate qapi_free_FOO()
function.  We already pass in "how to allocate" information: size.

> diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
> index bc8085d..d3466a4 100644
> --- a/tests/test-qmp-commands.c
> +++ b/tests/test-qmp-commands.c
> @@ -227,14 +227,13 @@ static void test_dealloc_partial(void)
>          QDECREF(ud2_dict);
>      }
>
> -    /* verify partial success */
> -    assert(ud2 != NULL);
> -    assert(ud2->string0 != NULL);
> -    assert(strcmp(ud2->string0, text) == 0);
> -    assert(ud2->dict1 == NULL);
> -
> -    /* confirm & release construction error */
> +    /* verify that visit_type_XXX() cleans up properly on error */
>      error_free_or_abort(&err);
> +    assert(!ud2);
> +
> +    /* Manually create a partial object, leaving ud2->dict1 at NULL */
> +    ud2 = g_new0(UserDefTwo, 1);
> +    ud2->string0 = g_strdup(text);
>
>      /* tear down partial object */
>      qapi_free_UserDefTwo(ud2);
> diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
> index 775ad39..4db35dd 100644
> --- a/tests/test-qmp-input-strict.c
> +++ b/tests/test-qmp-input-strict.c
> @@ -181,10 +181,7 @@ static void test_validate_fail_struct(TestInputVisitorData *data,
>
>      visit_type_TestStruct(v, NULL, &p, &err);
>      error_free_or_abort(&err);
> -    if (p) {
> -        g_free(p->string);
> -    }
> -    g_free(p);
> +    g_assert(!p);
>  }
>
>  static void test_validate_fail_struct_nested(TestInputVisitorData *data,
> @@ -198,7 +195,7 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data,
>
>      visit_type_UserDefTwo(v, NULL, &udp, &err);
>      error_free_or_abort(&err);
> -    qapi_free_UserDefTwo(udp);
> +    g_assert(!udp);
>  }
>
>  static void test_validate_fail_list(TestInputVisitorData *data,
> @@ -212,7 +209,7 @@ static void test_validate_fail_list(TestInputVisitorData *data,
>
>      visit_type_UserDefOneList(v, NULL, &head, &err);
>      error_free_or_abort(&err);
> -    qapi_free_UserDefOneList(head);
> +    g_assert(!head);
>  }
>
>  static void test_validate_fail_union_native_list(TestInputVisitorData *data,
> @@ -227,7 +224,7 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data,
>
>      visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err);
>      error_free_or_abort(&err);
> -    qapi_free_UserDefNativeListUnion(tmp);
> +    g_assert(!tmp);
>  }
>
>  static void test_validate_fail_union_flat(TestInputVisitorData *data,
> @@ -241,7 +238,7 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data,
>
>      visit_type_UserDefFlatUnion(v, NULL, &tmp, &err);
>      error_free_or_abort(&err);
> -    qapi_free_UserDefFlatUnion(tmp);
> +    g_assert(!tmp);
>  }
>
>  static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
> @@ -256,13 +253,13 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
>
>      visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err);
>      error_free_or_abort(&err);
> -    qapi_free_UserDefFlatUnion2(tmp);
> +    g_assert(!tmp);
>  }
>
>  static void test_validate_fail_alternate(TestInputVisitorData *data,
>                                           const void *unused)
>  {
> -    UserDefAlternate *tmp = NULL;
> +    UserDefAlternate *tmp;

Are you sure this is sound?

>      Visitor *v;
>      Error *err = NULL;
>
> @@ -270,7 +267,7 @@ static void test_validate_fail_alternate(TestInputVisitorData *data,
>
>      visit_type_UserDefAlternate(v, NULL, &tmp, &err);
>      error_free_or_abort(&err);
> -    qapi_free_UserDefAlternate(tmp);
> +    g_assert(!tmp);
>  }
>
>  static void do_test_validate_qmp_introspect(TestInputVisitorData *data,
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index cd5e765..0eef7e0 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -733,18 +733,12 @@ static void test_visitor_in_errors(TestInputVisitorData *data,
>
>      visit_type_TestStruct(v, NULL, &p, &err);
>      error_free_or_abort(&err);
> -    /* FIXME - a failed parse should not leave a partially-allocated p
> -     * for us to clean up; this could cause callers to leak memory. */
> -    g_assert(p->string == NULL);
> -
> -    g_free(p->string);
> -    g_free(p);
> +    g_assert(!p);
>
>      v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]");
>      visit_type_strList(v, NULL, &q, &err);
>      error_free_or_abort(&err);
> -    assert(q);
> -    qapi_free_strList(q);
> +    assert(!q);
>  }
>
>  static void test_visitor_in_wrong_type(TestInputVisitorData *data,

Now I've seen the complete patch, two more remarks:

* I think all the allocating methods should behave the same.  Right now,
  some store null on failure, and some don't.  For the latter to work,
  the value must be null initially (or else the dealloc visitor runs
  amok).

* Do we really need the new return value?  It looks like it's always
  equal to visit_is_input(v) && obj && *obj.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types Eric Blake
@ 2016-01-28 15:34   ` Markus Armbruster
  2016-01-28 17:23     ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-28 15:34 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Alexander Graf, qemu-devel, open list:sPAPR,
	marcandre.lureau, David Gibson

Eric Blake <eblake@redhat.com> writes:

> By sticking the next pointer first, we don't need a union with
> 64-bit padding for smaller types.  On 32-bit platforms, this
> can reduce the size of uint8List from 16 bytes (or 12, depending
> on whether 64-bit ints can tolerate 4-byte alignment) down to 8.
> It has no effect on 64-bit platforms (where alignment still
> dictates a 16-byte struct); but fewer anonymous unions is still
> a win in my book.
>
> However, this requires visit_start_list() and visit_next_list()
> to gain a size parameter, to know what size element to allocate.
>
> I debated about going one step further, to allow for fewer casts,
> by doing:
>     typedef GenericList GenericList;
>     struct GenericList {
>         GenericList *next;
>     };
>     struct FooList {
>         GenericList base;
>         Foo value;
>     };
> so that you convert to 'GenericList *' by '&foolist->base', and
> back by 'container_of(generic, GenericList, base)' (as opposed to
> the existing '(GenericList *)foolist' and '(FooList *)generic').
> But doing that would require hoisting the declaration of
> GenericList prior to inclusion of qapi-types.h, rather than its
> current spot in visitor.h; it also makes iteration a bit more
> verbose through 'foolist->base.next' instead of 'foolist->next'.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v9: no change
> v8: rebase to earlier changes
> v7: new patch; probably too invasive to be worth it, especially if
> we can't prove that our current size for uint8List is a bottleneck
> ---
>  hw/ppc/spapr_drc.c           |  2 +-
>  include/qapi/visitor-impl.h  |  5 +++--
>  include/qapi/visitor.h       | 39 +++++++++++++++++++--------------------
>  qapi/opts-visitor.c          |  9 +++++----
>  qapi/qapi-dealloc-visitor.c  |  5 +++--
>  qapi/qapi-visit-core.c       | 14 +++++++++-----
>  qapi/qmp-input-visitor.c     |  8 ++++----
>  qapi/qmp-output-visitor.c    |  5 +++--
>  qapi/string-input-visitor.c  |  9 +++++----
>  qapi/string-output-visitor.c |  5 +++--
>  scripts/qapi-types.py        |  5 +----
>  scripts/qapi-visit.py        |  4 ++--
>  12 files changed, 58 insertions(+), 52 deletions(-)
>
> diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
> index 41f2da0..0eba901 100644
> --- a/hw/ppc/spapr_drc.c
> +++ b/hw/ppc/spapr_drc.c
> @@ -299,7 +299,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
>              int i;
>              prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
>              name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
> -            visit_start_list(v, name, NULL, &err);
> +            visit_start_list(v, name, NULL, 0, &err);
>              if (err) {
>                  error_propagate(errp, err);
>                  return;
> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 8df4ba1..dbbbcdb 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -41,9 +41,10 @@ struct Visitor
>
>      /* Must be set */
>      bool (*start_list)(Visitor *v, const char *name, GenericList **list,
> -                       Error **errp);
> +                       size_t size, Error **errp);
>      /* Must be set */
> -    GenericList *(*next_list)(Visitor *v, GenericList *element, Error **errp);
> +    GenericList *(*next_list)(Visitor *v, GenericList *element, size_t size,
> +                              Error **errp);
>      /* Must be set */
>      void (*end_list)(Visitor *v);
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 4eae633..c446726 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -56,11 +56,8 @@
>   * created by the qapi generator. */
>  typedef struct GenericList
>  {
> -    union {
> -        void *value;
> -        uint64_t padding;
> -    };
>      struct GenericList *next;
> +    char padding[];
>  } GenericList;

Less trickery, I like it.

Member padding appears to be unused.

>
>  /**
> @@ -130,19 +127,19 @@ void visit_end_implicit_struct(Visitor *v);
>  /**
>   * Prepare to visit a list tied to an object key @name.
>   * @name will be NULL if this is visited as part of another list.
> - * Input visitors malloc a qapi List struct into *@list, or set it to
> - * NULL if there are no elements in the list; and output visitors
> - * expect *@list to point to the start of the list, if any.  On
> - * return, if *@list is non-NULL, the caller should enter a loop
> + * Input visitors malloc a qapi List struct into *@list of size @size,
> + * or set it to NULL if there are no elements in the list; and output
> + * visitors expect *@list to point to the start of the list, if any.
> + * On return, if *@list is non-NULL, the caller should enter a loop
>   * visiting the current element, then using visit_next_list() to
>   * advance to the next element, until that returns NULL; then
>   * visit_end_list() must be used to complete the visit.
>   *
> - * If supported by a visitor, @list can be NULL to indicate that there
> - * is no qapi List struct, and that the upcoming visit calls are
> - * parsing input to or creating output from some other representation;
> - * in this case, visit_next_list() will not be needed, but
> - * visit_end_list() is still mandatory.
> + * If supported by a visitor, @list can be NULL (and @size 0) to
> + * indicate that there is no qapi List struct, and that the upcoming
> + * visit calls are parsing input to or creating output from some other
> + * representation; in this case, visit_next_list() will not be needed,
> + * but visit_end_list() is still mandatory.
>   *
>   * Returns true if *@list was allocated; if that happens, and an error
>   * occurs any time before the matching visit_end_list(), then the
> @@ -150,17 +147,19 @@ void visit_end_implicit_struct(Visitor *v);
>   * allocation before returning control further.
>   */
>  bool visit_start_list(Visitor *v, const char *name, GenericList **list,
> -                      Error **errp);
> +                      size_t size, Error **errp);
>  /**
>   * Iterate over a GenericList during a list visit.
>   * Before calling this function, the caller should use the appropriate
> - * visit_type_FOO() for the current list element at @element->value, and
> - * check for errors. @element must not be NULL; on the first iteration,
> - * it should be the value in *list after visit_start_list(); on other
> - * calls it should be the previous return value.  This function
> - * returns NULL once there are no further list elements.
> + * visit_type_FOO() for the current list element at @element->value,
> + * and check for errors. @element must not be NULL; on the first
> + * iteration, it should be the value in *list after
> + * visit_start_list(); on other calls it should be the previous return
> + * value.  @size should be the same as for visit_start_list().  This
> + * function returns NULL once there are no further list elements.
>   */
> -GenericList *visit_next_list(Visitor *v, GenericList *element, Error **errp);
> +GenericList *visit_next_list(Visitor *v, GenericList *element, size_t size,
> +                             Error **errp);
>  /**
>   * Complete the list started earlier.
>   * Must be called after any successful use of visit_start_list(),
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 38d1e68..28f9a8a 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -214,7 +214,8 @@ lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>
>
>  static bool
> -opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
> +opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size,
> +                Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
>
> @@ -225,7 +226,7 @@ opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>      ov->repeated_opts = lookup_distinct(ov, name, errp);
>      if (ov->repeated_opts) {
>          ov->list_mode = LM_IN_PROGRESS;
> -        *list = g_new0(GenericList, 1);
> +        *list = g_malloc0(size);
>          return true;
>      } else {
>          *list = NULL;
> @@ -235,7 +236,7 @@ opts_start_list(Visitor *v, const char *name, GenericList **list, Error **errp)
>
>
>  static GenericList *
> -opts_next_list(Visitor *v, GenericList *list, Error **errp)
> +opts_next_list(Visitor *v, GenericList *list, size_t size, Error **errp)
>  {
>      OptsVisitor *ov = to_ov(v);
>
> @@ -269,7 +270,7 @@ opts_next_list(Visitor *v, GenericList *list, Error **errp)
>          abort();
>      }
>
> -    list->next = g_new0(GenericList, 1);
> +    list->next = g_malloc0(size);
>      return list->next;
>  }
>
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index 0990199..d498f29 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -90,7 +90,8 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v)
>  }
>
>  static bool qapi_dealloc_start_list(Visitor *v, const char *name,
> -                                    GenericList **list, Error **errp)
> +                                    GenericList **list, size_t size,
> +                                    Error **errp)
>  {
>      QapiDeallocVisitor *qov = to_qov(v);
>      qapi_dealloc_push(qov, NULL);
> @@ -98,7 +99,7 @@ static bool qapi_dealloc_start_list(Visitor *v, const char *name,
>  }
>
>  static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *list,
> -                                           Error **errp)
> +                                           size_t size, Error **errp)
>  {
>      GenericList *next = list->next;
>      g_free(list);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 259e0cb..ed4de71 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -80,19 +80,23 @@ void visit_end_implicit_struct(Visitor *v)
>  }
>
>  bool visit_start_list(Visitor *v, const char *name, GenericList **list,
> -                      Error **errp)
> +                      size_t size, Error **errp)
>  {
> -    bool result = v->start_list(v, name, list, errp);
> +    bool result;
> +
> +    assert(list ? size : !size);

Tighter than size != 0 would be size >= GenericList.  Same elsewhere.

> +    result = v->start_list(v, name, list, size, errp);
>      if (result) {
>          assert(list && *list);
>      }
>      return result;
>  }
>
> -GenericList *visit_next_list(Visitor *v, GenericList *list, Error **errp)
> +GenericList *visit_next_list(Visitor *v, GenericList *list, size_t size,
> +                             Error **errp)
>  {
> -    assert(list);
> -    return v->next_list(v, list, errp);
> +    assert(list && size);
> +    return v->next_list(v, list, size, errp);
>  }
>
>  void visit_end_list(Visitor *v)
[...]

Rest looks good.  Didn't look as closely as for the previous patches
(getting tired), but so far I like the idea.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes Eric Blake
@ 2016-01-28 15:37   ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-28 15:37 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Several commits have been changing the generator, but not updating
> the docs to match:
> - The implicit tag member is named "type", not "kind".  Screwed up in
> commit 39a1815.
> - Commit 9f08c8ec made list types lazy, and thereby dropped
> UserDefOneList if nothing explicitly uses the list type.
> - The parameter order has switched 'name' to occur earlier.
> - The generated code now checks for partial allocations.
> - etc.
>
> Rework the examples to show slightly more output (we don't want to
> show too much; that's what the testsuite is for), and regenerate the
> output to match all recent changes.
>
> Reported-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>
> ---
> v9: new patch
> ---
>  docs/qapi-code-gen.txt | 130 +++++++++++++++++++++++++++++--------------------
>  1 file changed, 78 insertions(+), 52 deletions(-)
>
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index 128f074..ae7b1a5 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -1,7 +1,7 @@
>  = How to use the QAPI code generator =
>
>  Copyright IBM Corp. 2011
> -Copyright (C) 2012-2015 Red Hat, Inc.
> +Copyright (C) 2012-2016 Red Hat, Inc.
>
>  This work is licensed under the terms of the GNU GPL, version 2 or
>  later. See the COPYING file in the top-level directory.
> @@ -655,7 +655,7 @@ Union types
>
>      { "name": "BlockdevOptions", "meta-type": "object",
>        "members": [
> -          { "name": "kind", "type": "BlockdevOptionsKind" } ],
> +          { "name": "type", "type": "BlockdevOptionsKind" } ],
>        "tag": "type",
>        "variants": [
>            { "case": "file", "type": ":obj-FileOptions-wrapper" },
> @@ -721,29 +721,34 @@ the names of built-in types.  Clients should examine member
>
>  == Code generation ==
>
> -Schemas are fed into four scripts to generate all the code/files that,
> +Schemas are fed into five scripts to generate all the code/files that,
>  paired with the core QAPI libraries, comprise everything required to
>  take JSON commands read in by a Client JSON Protocol server, unmarshal
>  the arguments into the underlying C types, call into the corresponding
> -C function, and map the response back to a Client JSON Protocol
> -response to be returned to the user.
> +C function, map the response back to a Client JSON Protocol response
> +to be returned to the user, and introspect the commands.
>
> -As an example, we'll use the following schema, which describes a single
> -complex user-defined type (which will produce a C struct, along with a list
> -node structure that can be used to chain together a list of such types in
> -case we want to accept/return a list of this type with a command), and a
> -command which takes that type as a parameter and returns the same type:
> +As an example, we'll use the following schema, which describes a
> +single complex user-defined type, along with command which takes a
> +list of that type as a parameter.

Lost: and returns the same type

>                                     The user is responsible for writing
> +the implementation of qmp_my_command(); everything else is produced by
> +the generator.
>
>      $ cat example-schema.json
>      { 'struct': 'UserDefOne',
> -      'data': { 'integer': 'int', 'string': 'str' } }
> +      'data': { 'integer': 'int', '*string': 'str' } }
>
>      { 'command': 'my-command',
> -      'data':    {'arg1': 'UserDefOne'},
> +      'data':    { 'arg1': ['UserDefOne'] },
>        'returns': 'UserDefOne' }
>
>      { 'event': 'MY_EVENT' }
>
> +For a more thorough look at generated code, the testsuite includes
> +tests/qapi-schema/qapi-schema-tests.json that covers more examples of
> +what the generator will accept, and compiles the resulting C code as
> +part of 'make check-unit'.
> +

Good idea.

Remaining changes are all to example output.  I'll check them when we're
closer to merging this series.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects
  2016-01-28 15:24   ` Markus Armbruster
@ 2016-01-28 17:05     ` Eric Blake
  2016-01-29 12:03       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-28 17:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 10576 bytes --]

On 01/28/2016 08:24 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Returning a partial object on error is an invitation for a careless
>> caller to leak memory.  As no one outside the testsuite was actually
>> relying on these semantics, it is cleaner to just document and
>> guarantee that ALL pointer-based visit_type_FOO() functions always
>> leave a safe value in *obj during an input visitor (either the new
>> object on success, or NULL if an error is encountered).
>>
>> Since input visitors have blind assignment semantics, we have to
>> track the result of whether an assignment is made all the way down
>> to each visitor callback implementation, to avoid making decisions
>> based on potentially uninitialized storage.
> 
> I'm not sure I get this paragraph.  What's "blind assignment semantics"?

The caller does:

{
    Foo *bar; /* uninit */
    visit_type_Foo(&bar);
    if (no error) {
        /* use bar */
    }
}

Which means the visitor core can't do 'if (*obj)', because obj may be
uninitialized on entry; if it dereferences obj at all, it must be via
'*obj = ...' which I'm terming a blind assignment.

But I can try and come up with better wording.

> 
>> Note that we still leave *obj unchanged after a scalar-based
>> visit_type_FOO(); I did not feel like auditing all uses of
>> visit_type_Enum() to see if the callers would tolerate a specific
>> sentinel value (not to mention having to decide whether it would
>> be better to use 0 or ENUM__MAX as that sentinel).
> 
> The assigning input visitor functions (core and generated) all assign
> either a pointer to a newly allocated object, or a non-pointer scalar
> value.
> 
> Possible behaviors on error:
> 
> (0) What we have now: assign something that must be cleaned up with the
>     dealloc visitor if it's a pointer, but is otherwise useless
> 
>     CON: callers have to clean up
>     CON: exposes careless callers to useless values
> 
> (1) Don't assign anything
> 
>     PRO: consistent
>     CON: exposes careless callers to uninitialized values

Half-PRO: Caller can pre-initialize a default, and rely on that value on
error.  In fact, I think we have callers doing that when visiting an
enum, and I didn't feel up to auditing them all when first writing the
patch.

But a small audit right now shows:

qom/object.c:object_property_get_enum() starts with uninitialized 'int
ret;', hardcodes 'return 0;' on some failures, but otherwise passes it
to visit_type_enum() then blindly returns that value even if errp is
set.  Yuck.  Callers HAVE to check errp rather than relying on the
return value to flag errors; although it looks like the lone caller is
in numa.c and passes &error_abort.

Maybe I should just bite the bullet, and audit ALL uses of visitor for
their behavior of what to expect in *obj on error.

> 
> (2) Assign zero bits
> 
>     PRO: consistent
>     CON: exposes careless callers to bogus zero values

Half-CON: Caller cannot pre-initialize a default

> 
> (3) Assign null pointer, else don't assign anything
> 
>     CON: inconsistent
>     CON: mix of (1)'s and (2)'s CON

Which I think is what I did in this patch.

> 
> (4) Other ideas?

Store the caller's initial value (often all zero, but not necessarily),
and restore THAT value on error (preserves a pre-initialized default,
but leaves uninitialized garbage in place to bite careless callers)

> 
> Note that *obj is almost always null on entry, because we allocate
> objects zero-initialized.  Only root visits can expose their caller to
> uninitialized values.
> 
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>

>> +/* All qapi types have a corresponding function with a signature
>> + * roughly compatible with this:
>> + *
>> + * void visit_type_FOO(Visitor *v, void *obj, const char *name, Error **errp);
>> + *
>> + * where *@obj is itself a pointer or a scalar.  The visit functions for
>> + * built-in types are declared here, while the functions for qapi-defined
>> + * struct, union, enum, and list types are generated (see qapi-visit.h).
>> + * Input visitors may receive an uninitialized *@obj, and guarantee a
>> + * safe value is assigned (a new object on success, or NULL on failure).
> 
> This specifies the safe value: NULL.  Makes no sense for non-pointer
> scalars.
> 
> May input visitors really receive uninitialized *@obj?  As far as I can
> see, we routinely store a newly allocated object to *@obj on success,
> and store nothing on failure.  To be able to pass this to the dealloc
> visitor, *@obj must have been null initially, mustn't it?

Correct.  Pre-patch: either the caller pre-initialized obj to NULL (and
can then blindly pass it to the dealloc visitor regardless of whether
visit_start_struct() succeeded), or it did not (in which case the
dealloc visitor must not be called if *obj remains uninitialized because
visit_start_struct() failed, but MUST be called if visit_start_struct()
succeeded to clean up the partial object).

Post-patch: calling visit_start_struct() always assigns to *obj, and the
dealloc visitor can be safely called.

> 
>> + * Output visitors expect *@obj to be properly initialized on entry.
> 
> What about the dealloc visitor?

Can be passed NULL, a partial object, or a complete object.  But
spelling that out would indeed be useful.

> 
>> + *
>> + * Additionally, all qapi structs have a generated function compatible
>> + * with this:
>> + *
>> + * void qapi_free_FOO(void *obj);
>> + *
>> + * which behaves like free(), even if @obj is NULL or was only partially
>> + * allocated before encountering an error.
> 
> Will partially allocated QAPI objects remain visible outside input
> visitors?

A user can still hand-initialize a QAPI struct into partial state,
although you are correct that this patch is what is changing things to
no longer leak a partial object outside of the visit_type_FOO() calls.

>> + * Returns true if *@obj was allocated; if that happens, and an error
>> + * occurs any time before the matching visit_end_struct(), then the
>> + * caller (usually a visit_type_FOO() function) knows to undo the
>> + * allocation before returning control further.
>>   */
>> -void visit_start_struct(Visitor *v, const char *name, void **obj,
>> +bool visit_start_struct(Visitor *v, const char *name, void **obj,
>>                          size_t size, Error **errp);
> 
> Need to see how this is used before I can judge whether I like it :)
> 
>> @@ -152,6 +154,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>>          ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
>>          opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
>>      }
>> +    return result;
>>  }
> 
> Stores the newly allocated object in *@obj on success, doesn't store
> anything on failure.
> 
> To make cleanup possible, *@obj must be null initially.  Then the return
> value is true iff *@obj is non-null on return.

If we want, I can change these to all store *obj = NULL on failure.
Thinking about it more: for any given visit_type_FOO(), if
visit_start_struct() succeeds but something else later fails, *obj will
be NULL; so it also makes sense that if visit_start_struct() fails than
*obj should be NULL.

>> -void visit_start_struct(Visitor *v, const char *name, void **obj,
>> +bool visit_start_struct(Visitor *v, const char *name, void **obj,
>>                          size_t size, Error **errp)
>>  {
>> +    bool result;
>> +
>>      assert(obj ? size : !size);
>>      if (obj && visit_is_output(v)) {
>>          assert(*obj);
>>      }
>> -    v->start_struct(v, name, obj, size, errp);
>> +    result = v->start_struct(v, name, obj, size, errp);
>> +    if (result) {
>> +        assert(obj && *obj);
>> +    }
> 
> Roundabout way to write assert(!result || (obj && *obj));
> 
> Heh, you even assert one half of what I'm trying to prove.
> 
> Can we make it assert(result == (visit_is_input(v) && obj && *obj) ?

Missing a ); guessing that you meant:

assert(result == (visit_is_input(v) && obj && *obj));

Yes, I think we can, but we'd need a visit_is_input() helper.

>> @@ -236,7 +253,11 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>>          assert(*obj);
>>      }
>>       */
>> -    v->type_str(v, name, obj, errp);
>> +    v->type_str(v, name, obj, &err);
>> +    if (!visit_is_output(v) && err) {
>> +        *obj = NULL;
> 
> Shouldn't storing null on error be left to v->type_str(), like we do for
> structs and lists?  Not least because it begs the question whether this
> leaks a string stored by it.

May be worthwhile to mandate that tighter semantics on each visitor.

>> +++ b/scripts/qapi-visit.py

>> @@ -301,10 +316,15 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>>      visit_check_struct(v, &err);
>>  out_obj:
>>      visit_end_struct(v);
>> +    if (allocated && err) {
>> +        qapi_free_%(c_name)s(*obj);
>> +        *obj = NULL;
>> +    }
>>  out:
>>      error_propagate(errp, err);
>>  }
>> -''')
>> +''',
>> +                 c_name=c_name(name))
>>
>>      return ret
>>
> 
> Hmm.
> 
> This grows qapi-visit.c by 14% for me.
> 
> Can we do the deallocation in the visitor core somehow?  We'd have to
> pass "how to deallocate" information: the appropriate qapi_free_FOO()
> function.  We already pass in "how to allocate" information: size.

I thought about that; do we really want to be type-punning functions
like that?  But it does sound smaller; I can play with the idea.


> 
> Now I've seen the complete patch, two more remarks:
> 
> * I think all the allocating methods should behave the same.  Right now,
>   some store null on failure, and some don't.  For the latter to work,
>   the value must be null initially (or else the dealloc visitor runs
>   amok).

Agree; I'm leaning towards ALL allocating methods must store into *obj
(either NULL on failure, or object on success).

> 
> * Do we really need the new return value?  It looks like it's always
>   equal to visit_is_input(v) && obj && *obj.

Except we don't (yet) have a visit_is_input() function, let alone one
visible from within the generated qapi-visit.c code.  Maybe doing that
first would help.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types
  2016-01-28 15:34   ` Markus Armbruster
@ 2016-01-28 17:23     ` Eric Blake
  2016-01-29  8:19       ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-01-28 17:23 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Michael Roth, Alexander Graf, qemu-devel, open list:sPAPR,
	marcandre.lureau, David Gibson

[-- Attachment #1: Type: text/plain, Size: 2438 bytes --]

On 01/28/2016 08:34 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> By sticking the next pointer first, we don't need a union with
>> 64-bit padding for smaller types.  On 32-bit platforms, this
>> can reduce the size of uint8List from 16 bytes (or 12, depending
>> on whether 64-bit ints can tolerate 4-byte alignment) down to 8.
>> It has no effect on 64-bit platforms (where alignment still
>> dictates a 16-byte struct); but fewer anonymous unions is still
>> a win in my book.
>>
>> However, this requires visit_start_list() and visit_next_list()
>> to gain a size parameter, to know what size element to allocate.
>>
>> I debated about going one step further, to allow for fewer casts,
>> by doing:
>>     typedef GenericList GenericList;
>>     struct GenericList {
>>         GenericList *next;
>>     };
>>     struct FooList {
>>         GenericList base;
>>         Foo value;
>>     };
>> so that you convert to 'GenericList *' by '&foolist->base', and
>> back by 'container_of(generic, GenericList, base)' (as opposed to
>> the existing '(GenericList *)foolist' and '(FooList *)generic').
>> But doing that would require hoisting the declaration of
>> GenericList prior to inclusion of qapi-types.h, rather than its
>> current spot in visitor.h; it also makes iteration a bit more
>> verbose through 'foolist->base.next' instead of 'foolist->next'.

Should I attempt this?


>>  typedef struct GenericList
>>  {
>> -    union {
>> -        void *value;
>> -        uint64_t padding;
>> -    };
>>      struct GenericList *next;
>> +    char padding[];
>>  } GenericList;
> 
> Less trickery, I like it.
> 
> Member padding appears to be unused.

or just leave it at this?

>>  bool visit_start_list(Visitor *v, const char *name, GenericList **list,
>> -                      Error **errp)
>> +                      size_t size, Error **errp)
>>  {
>> -    bool result = v->start_list(v, name, list, errp);
>> +    bool result;
>> +
>> +    assert(list ? size : !size);
> 
> Tighter than size != 0 would be size >= GenericList.  Same elsewhere.

Makes sense.

> 
> Rest looks good.  Didn't look as closely as for the previous patches
> (getting tired), but so far I like the idea.

Okay, I'll keep it and drop the RFC.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (36 preceding siblings ...)
  2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes Eric Blake
@ 2016-01-28 17:56 ` Markus Armbruster
  37 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-28 17:56 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Eduardo Habkost

Eric Blake <eblake@redhat.com> writes:

> Based on qemu.git master. Pending prerequisites:
> + Not a strong dependency, but for qapi-tests to consistently pass,
> I needed a race fixed:
> https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01827.html

Commit a174da3.

Series needs a rebase now.  I figure there are enough actionable review
insights to warrant a full respin.

However, the series' length makes review taxing.  Could you try to split
off an easier part for hopefully quick merging?

Non-easy topics, from memory:

* Retrofitting a contract to visitors.

* Shall we require visit_end_FOO() even after error?  I have to admit
  review dampened my enthusiasm for this idea.

* PATCH 30+

[...]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event use of qapi visitor
  2016-01-20 15:19   ` Markus Armbruster
  2016-01-20 17:10     ` Eric Blake
@ 2016-01-28 22:51     ` Eric Blake
  1 sibling, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-28 22:51 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1197 bytes --]

On 01/20/2016 08:19 AM, Markus Armbruster wrote:
> 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.
> 

>> +++ 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'):
> 
> Probably clearer than label=None, but duplicates gen_err_check()'s
> default.  Fine with me.

Use of label=None resulted in literal "goto None;" in the generated file
(Python stringized it, rather than treating it as a hint to behave as if
the argument were not supplied).  So yes, I had to duplicate the default
value in both methods, to avoid having to do 'if label:' within the
implementations.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root
  2016-01-21 13:58   ` Markus Armbruster
@ 2016-01-29  3:06     ` Eric Blake
  0 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-29  3:06 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 3805 bytes --]

On 01/21/2016 06:58 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> The previous commit documented an inconsistency in how we are
>> using the stack of qmp-output-visitor.  Normally, pushing a
>> single top-level object puts the object on the stack twice:
>> once as the root, and once as the current container being
>> appended to; but popping that struct only pops once.  However,
>> qmp_ouput_add() was trying to either set up the added object
>> as the new root (works if you parse two top-level scalars in a
>> row: the second replaces the first as the root) or as a member
>> of the current container (works as long as you have an open
>> container on the stack; but if you have popped the first
>> top-level container, it then resolves to the root and still
>> tries to add into that existing container).
>>
>> Fix the stupidity by not tracking two separate things in the
>> stack.  Drop the now-useless qmp_output_first() while at it.
>>
>> Saved for a later patch: we still are rather sloppy in that
>> qmp_output_get_object() can be called in the middle of a parse,
>> rather than requiring that a visit is complete.
>>

>> +        switch (qobject_type(cur)) {
>> +        case QTYPE_QDICT:
>> +            assert(name);
>> +            qdict_put_obj(qobject_to_qdict(cur), name, value);
>> +            break;
>> +        case QTYPE_QLIST:
>> +            qlist_append_obj(qobject_to_qlist(cur), value);
>> +            break;
>> +        default:
>> +            g_assert_not_reached();
> 
> We usually just abort().

But there are definitely existing examples, and it is a bit more
self-documenting.


>>
>> @@ -230,7 +205,9 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
>>  /* Finish building, and return the root object. Will not be NULL. */
>>  QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
>>  {
>> -    QObject *obj = qmp_output_first(qov);
>> +    /* FIXME: we should require that a visit occurred, and that it is
>> +     * complete (no starts without a matching end) */
> 
> I agree the visit must complete before you can retrieve the value.
> 
> I think there are two sane ways to recover from errors:
> 
> 1. Require the client to empty the stack by calling the necessary end
>    methods.
> 
> 2. Allow the client to reset or destroy the visitor without calling end
>    methods.
> 
> *This* visitor would be fine with either.  I guess the others would be
> fine, too.  So it's a question of interface design.
> 
> I'm currently leaning towards 2, because "you must do A, B and C before
> you can destroy this object" would be weird.  What do you think?

Patches later in the series revisit the question, and adding a reset
also makes it more interesting to be able to reset at any point in a
partial visit.  For _this_ patch, I think we're okay, but this is
probably the cutoff for what I'm sending in the first round of v10,
saving all the trickier stuff for later.

> 
>> +    QObject *obj = qov->root;
>>      if (obj) {
>>          qobject_incref(obj);
>>      } else {
>> @@ -248,16 +225,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
>>  {
>>      QStackEntry *e, *tmp;
>>
>> -    /* The bottom QStackEntry, if any, owns the root QObject. See the
>> -     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
>> -    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
>> -
> 
> If we require end methods to be called, the stack must be empty here,
> rendering the following loop useless.

Yeah, an interesting observation that will affect what I do in 24/37.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types
  2016-01-28 17:23     ` Eric Blake
@ 2016-01-29  8:19       ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-01-29  8:19 UTC (permalink / raw)
  To: Eric Blake
  Cc: Alexander Graf, qemu-devel, Michael Roth, open list:sPAPR,
	marcandre.lureau, David Gibson

Eric Blake <eblake@redhat.com> writes:

> On 01/28/2016 08:34 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> By sticking the next pointer first, we don't need a union with
>>> 64-bit padding for smaller types.  On 32-bit platforms, this
>>> can reduce the size of uint8List from 16 bytes (or 12, depending
>>> on whether 64-bit ints can tolerate 4-byte alignment) down to 8.
>>> It has no effect on 64-bit platforms (where alignment still
>>> dictates a 16-byte struct); but fewer anonymous unions is still
>>> a win in my book.
>>>
>>> However, this requires visit_start_list() and visit_next_list()
>>> to gain a size parameter, to know what size element to allocate.
>>>
>>> I debated about going one step further, to allow for fewer casts,
>>> by doing:
>>>     typedef GenericList GenericList;
>>>     struct GenericList {
>>>         GenericList *next;
>>>     };
>>>     struct FooList {
>>>         GenericList base;
>>>         Foo value;
>>>     };
>>> so that you convert to 'GenericList *' by '&foolist->base', and
>>> back by 'container_of(generic, GenericList, base)' (as opposed to
>>> the existing '(GenericList *)foolist' and '(FooList *)generic').
>>> But doing that would require hoisting the declaration of
>>> GenericList prior to inclusion of qapi-types.h, rather than its
>>> current spot in visitor.h; it also makes iteration a bit more
>>> verbose through 'foolist->base.next' instead of 'foolist->next'.
>
> Should I attempt this?

A quick grep for '(GenericList' finds two in qapi-code-gen.txt, and two
in qapi-visit-py.  I doubt avoiding them is worth much of your time or
mine :)

>>>  typedef struct GenericList
>>>  {
>>> -    union {
>>> -        void *value;
>>> -        uint64_t padding;
>>> -    };
>>>      struct GenericList *next;
>>> +    char padding[];
>>>  } GenericList;
>> 
>> Less trickery, I like it.
>> 
>> Member padding appears to be unused.
>
> or just leave it at this?

I'd say good enough.

>>>  bool visit_start_list(Visitor *v, const char *name, GenericList **list,
>>> -                      Error **errp)
>>> +                      size_t size, Error **errp)
>>>  {
>>> -    bool result = v->start_list(v, name, list, errp);
>>> +    bool result;
>>> +
>>> +    assert(list ? size : !size);
>> 
>> Tighter than size != 0 would be size >= GenericList.  Same elsewhere.
>
> Makes sense.
>
>> 
>> Rest looks good.  Didn't look as closely as for the previous patches
>> (getting tired), but so far I like the idea.
>
> Okay, I'll keep it and drop the RFC.

Thanks!

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects
  2016-01-28 17:05     ` Eric Blake
@ 2016-01-29 12:03       ` Markus Armbruster
  2016-01-29 15:13         ` Eric Blake
  0 siblings, 1 reply; 128+ messages in thread
From: Markus Armbruster @ 2016-01-29 12:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/28/2016 08:24 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Returning a partial object on error is an invitation for a careless
>>> caller to leak memory.  As no one outside the testsuite was actually
>>> relying on these semantics, it is cleaner to just document and
>>> guarantee that ALL pointer-based visit_type_FOO() functions always
>>> leave a safe value in *obj during an input visitor (either the new
>>> object on success, or NULL if an error is encountered).
>>>
>>> Since input visitors have blind assignment semantics, we have to
>>> track the result of whether an assignment is made all the way down
>>> to each visitor callback implementation, to avoid making decisions
>>> based on potentially uninitialized storage.
>> 
>> I'm not sure I get this paragraph.  What's "blind assignment semantics"?
>
> The caller does:
>
> {
>     Foo *bar; /* uninit */
>     visit_type_Foo(&bar);
>     if (no error) {
>         /* use bar */
>     }
> }
>
> Which means the visitor core can't do 'if (*obj)', because obj may be
> uninitialized on entry; if it dereferences obj at all, it must be via
> '*obj = ...' which I'm terming a blind assignment.
>
> But I can try and come up with better wording.

I'd suggest one, but I think we should first make up our minds on error
behavior.

>>> Note that we still leave *obj unchanged after a scalar-based
>>> visit_type_FOO(); I did not feel like auditing all uses of
>>> visit_type_Enum() to see if the callers would tolerate a specific
>>> sentinel value (not to mention having to decide whether it would
>>> be better to use 0 or ENUM__MAX as that sentinel).
>> 
>> The assigning input visitor functions (core and generated) all assign
>> either a pointer to a newly allocated object, or a non-pointer scalar
>> value.
>> 
>> Possible behaviors on error:
>> 
>> (0) What we have now: assign something that must be cleaned up with the
>>     dealloc visitor if it's a pointer, but is otherwise useless
>> 
>>     CON: callers have to clean up
>>     CON: exposes careless callers to useless values
>> 
>> (1) Don't assign anything

Need to be very careful to store only when success is assured.

Consider visiting a QAPI type that is actually two levels of containers,
say a list of a struct of scalars.  The visit is a walk of this tree
then:

                list
            ___/ ... \___
           /             \
        struct1          structN
       /  ...  \         /  ...  \   
    scal11   scal1M   scalN1   scalNM

Now let's consider the state when the visit reaches scalN1:

* The visits of scal11..scal1M and struct 1 have all succeeded already,
  and stored their value into their container.  Same for the visits of
  structI (I < N) omitted in the diagram, and their scalars.

* The visit of list and struct N are in progress: the object has been
  partially constructed, but not yet stored.

* The remaining visits haven't begun, and their members in objects
  already allocated are still zero.

Now the visit of scalN1 fails.  The visit of structN fails in turn,
freeing its (not yet stored, partially constructed) object.  Finally,
the visit of list fails, freeing its object.

That the failed visits of scalN1 and structN didn't store is actually
unimportant.

Of course, this isn't how things work now.  Visits of containers store
the newly allocated object before visiting members, in visit_start_*(),
and the member visits use that to find the object.

>>     PRO: consistent
>>     CON: exposes careless callers to uninitialized values
>
> Half-PRO: Caller can pre-initialize a default, and rely on that value on
> error.  In fact, I think we have callers doing that when visiting an
> enum, and I didn't feel up to auditing them all when first writing the
> patch.
>
> But a small audit right now shows:
>
> qom/object.c:object_property_get_enum() starts with uninitialized 'int
> ret;', hardcodes 'return 0;' on some failures, but otherwise passes it
> to visit_type_enum() then blindly returns that value even if errp is
> set.  Yuck.  Callers HAVE to check errp rather than relying on the
> return value to flag errors; although it looks like the lone caller is
> in numa.c and passes &error_abort.
>
> Maybe I should just bite the bullet, and audit ALL uses of visitor for
> their behavior of what to expect in *obj on error.
>> 
>> (2) Assign zero bits
>> 
>>     PRO: consistent
>>     CON: exposes careless callers to bogus zero values
>
> Half-CON: Caller cannot pre-initialize a default

With (1) don't assign, the caller can pick an error value by assigning
it before the visit, and it must not access the value on error unless it
does.

With (2) assign zero, the caller can't pick an error value, but may
safely access the value even on error.

Tradeoff.  I figure either can work for us.

>> (3) Assign null pointer, else don't assign anything
>> 
>>     CON: inconsistent
>>     CON: mix of (1)'s and (2)'s CON
>
> Which I think is what I did in this patch.

I don't like the inconsistency.  It complicates the interface.

>> (4) Other ideas?
>
> Store the caller's initial value (often all zero, but not necessarily),
> and restore THAT value on error (preserves a pre-initialized default,
> but leaves uninitialized garbage in place to bite careless callers)

Behaves like (1).  Under the hood, it replaces "store only when success
is assured" by "undo the store on failure".  I'd guess this would be
more complicated.

>> Note that *obj is almost always null on entry, because we allocate
>> objects zero-initialized.  Only root visits can expose their caller to
>> uninitialized values.
>> 
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>>
>
>>> +/* All qapi types have a corresponding function with a signature
>>> + * roughly compatible with this:
>>> + *
>>> + * void visit_type_FOO(Visitor *v, void *obj, const char *name, Error **errp);
>>> + *
>>> + * where *@obj is itself a pointer or a scalar.  The visit functions for
>>> + * built-in types are declared here, while the functions for qapi-defined
>>> + * struct, union, enum, and list types are generated (see qapi-visit.h).
>>> + * Input visitors may receive an uninitialized *@obj, and guarantee a
>>> + * safe value is assigned (a new object on success, or NULL on failure).
>> 
>> This specifies the safe value: NULL.  Makes no sense for non-pointer
>> scalars.
>> 
>> May input visitors really receive uninitialized *@obj?  As far as I can
>> see, we routinely store a newly allocated object to *@obj on success,
>> and store nothing on failure.  To be able to pass this to the dealloc
>> visitor, *@obj must have been null initially, mustn't it?
>
> Correct.  Pre-patch: either the caller pre-initialized obj to NULL (and
> can then blindly pass it to the dealloc visitor regardless of whether
> visit_start_struct() succeeded),

Yes.

Three cases:

(a) visit_start_struct() succeeds: it stored to *obj, and caller must
clean it up.

(b) visit_start_struct() fails without storing to *obj: *obj is still
null, and caller may clean it up (it's a nop).  Whatever
visit_start_struct() allocated, it has to clean up itself.

(c) visit_start_struct() fails with storing to *obj: caller must clean
it up.

Cleanup is relatively easy for the caller: just do it always.

But what if caller forgets to initialize to null?  That's next:

>                                  or it did not (in which case the
> dealloc visitor must not be called if *obj remains uninitialized because
> visit_start_struct() failed, but MUST be called if visit_start_struct()
> succeeded to clean up the partial object).

Case (b) changes:

(b) visit_start_struct() fails without storing to *obj: caller must not
use *obj.  Whatever visit_start_struct() allocated, it has to clean up
itself.

I can't see how the caller could get cleanup right without initializing
to null.

> Post-patch: calling visit_start_struct() always assigns to *obj, and the
> dealloc visitor can be safely called.

Actually, it always assigns *pointers*.  That's enough to permit
unconditional cleanup, of course.

>>> + * Output visitors expect *@obj to be properly initialized on entry.
>> 
>> What about the dealloc visitor?
>
> Can be passed NULL, a partial object, or a complete object.  But
> spelling that out would indeed be useful.
>
>> 
>>> + *
>>> + * Additionally, all qapi structs have a generated function compatible
>>> + * with this:
>>> + *
>>> + * void qapi_free_FOO(void *obj);
>>> + *
>>> + * which behaves like free(), even if @obj is NULL or was only partially
>>> + * allocated before encountering an error.
>> 
>> Will partially allocated QAPI objects remain visible outside input
>> visitors?
>
> A user can still hand-initialize a QAPI struct into partial state,
> although you are correct that this patch is what is changing things to
> no longer leak a partial object outside of the visit_type_FOO() calls.

Okay.

>>> + * Returns true if *@obj was allocated; if that happens, and an error
>>> + * occurs any time before the matching visit_end_struct(), then the
>>> + * caller (usually a visit_type_FOO() function) knows to undo the
>>> + * allocation before returning control further.
>>>   */
>>> -void visit_start_struct(Visitor *v, const char *name, void **obj,
>>> +bool visit_start_struct(Visitor *v, const char *name, void **obj,
>>>                          size_t size, Error **errp);
>> 
>> Need to see how this is used before I can judge whether I like it :)
>> 
>>> @@ -152,6 +154,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>>>          ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
>>>          opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
>>>      }
>>> +    return result;
>>>  }
>> 
>> Stores the newly allocated object in *@obj on success, doesn't store
>> anything on failure.
>> 
>> To make cleanup possible, *@obj must be null initially.  Then the return
>> value is true iff *@obj is non-null on return.
>
> If we want, I can change these to all store *obj = NULL on failure.
> Thinking about it more: for any given visit_type_FOO(), if
> visit_start_struct() succeeds but something else later fails, *obj will
> be NULL; so it also makes sense that if visit_start_struct() fails than
> *obj should be NULL.

I think behavior (1) don't assign and (2) assign zero both work, we just
have to pick one and run with it.

If we pick behavior (1) don't assign, then we should assert something
like !obj || !*obj on entry.  With such assertions in place, I think (1)
should be roughly as safe as (2).

>>> -void visit_start_struct(Visitor *v, const char *name, void **obj,
>>> +bool visit_start_struct(Visitor *v, const char *name, void **obj,
>>>                          size_t size, Error **errp)
>>>  {
>>> +    bool result;
>>> +
>>>      assert(obj ? size : !size);
>>>      if (obj && visit_is_output(v)) {
>>>          assert(*obj);
>>>      }
>>> -    v->start_struct(v, name, obj, size, errp);
>>> +    result = v->start_struct(v, name, obj, size, errp);
>>> +    if (result) {
>>> +        assert(obj && *obj);
>>> +    }
>> 
>> Roundabout way to write assert(!result || (obj && *obj));
>> 
>> Heh, you even assert one half of what I'm trying to prove.
>> 
>> Can we make it assert(result == (visit_is_input(v) && obj && *obj) ?
>
> Missing a ); guessing that you meant:
>
> assert(result == (visit_is_input(v) && obj && *obj));

Yes.

> Yes, I think we can, but we'd need a visit_is_input() helper.

Right now, I'm not proposing to change the assertion, I'm only trying to
find out whether we actually need the return value.

>>> @@ -236,7 +253,11 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>>>          assert(*obj);
>>>      }
>>>       */
>>> -    v->type_str(v, name, obj, errp);
>>> +    v->type_str(v, name, obj, &err);
>>> +    if (!visit_is_output(v) && err) {
>>> +        *obj = NULL;
>> 
>> Shouldn't storing null on error be left to v->type_str(), like we do for
>> structs and lists?  Not least because it begs the question whether this
>> leaks a string stored by it.
>
> May be worthwhile to mandate that tighter semantics on each visitor.

Yes, please.  If we adopt consistent behaviors (1) or (2), a general
rule will cover them all.

>>> +++ b/scripts/qapi-visit.py
>
>>> @@ -301,10 +316,15 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>>>      visit_check_struct(v, &err);
>>>  out_obj:
>>>      visit_end_struct(v);
>>> +    if (allocated && err) {
>>> +        qapi_free_%(c_name)s(*obj);
>>> +        *obj = NULL;
>>> +    }
>>>  out:
>>>      error_propagate(errp, err);
>>>  }
>>> -''')
>>> +''',
>>> +                 c_name=c_name(name))
>>>
>>>      return ret
>>>
>> 
>> Hmm.
>> 
>> This grows qapi-visit.c by 14% for me.
>> 
>> Can we do the deallocation in the visitor core somehow?  We'd have to
>> pass "how to deallocate" information: the appropriate qapi_free_FOO()
>> function.  We already pass in "how to allocate" information: size.
>
> I thought about that; do we really want to be type-punning functions
> like that?  But it does sound smaller; I can play with the idea.

Type-punning functions is usually a bad idea.  Calling the result has
undefined behavior unless the two function types are compatible.  The
function types at hand differ only in the parameter type, but void *
would not be compatible with some struct QAPIType *.

As long as the two pointer types have the same representation, the
compiler would have to work to actually screw this up.

A perfectly clean solution would need a wrapper function, negating some
of the code savings.

>> Now I've seen the complete patch, two more remarks:
>> 
>> * I think all the allocating methods should behave the same.  Right now,
>>   some store null on failure, and some don't.  For the latter to work,
>>   the value must be null initially (or else the dealloc visitor runs
>>   amok).
>
> Agree; I'm leaning towards ALL allocating methods must store into *obj
> (either NULL on failure, or object on success).

If this was a function return value, correct behavior would be obvious:
return object on success, return null on failure.  Equivalent to (2).
But this is a side effect.  There, I rather like "do nothing on
failure".  That's (1).

But "rather like" doesn't justify implementation complexity.  If (2)
turns out to be simpler, go for it.

>> * Do we really need the new return value?  It looks like it's always
>>   equal to visit_is_input(v) && obj && *obj.
>
> Except we don't (yet) have a visit_is_input() function, let alone one
> visible from within the generated qapi-visit.c code.  Maybe doing that
> first would help.

If we move the destruction into the core, this problem goes away:

    void visit_type_T(Visitor *v, const char *name, T **obj, Error **errp)
    {
        Error *err = NULL;

        visit_start_struct(v, name, (void **)obj, sizeof(T), &err);
        if (err) {
            goto out;
        }
        if (!*obj) {
            goto out_obj;
        }
        visit_type_T_fields(v, obj, &err);
        if (err) {
            goto out_obj;
        }
        visit_check_struct(v, &err);
    out_obj:
        visit_end_struct(v, qapi_free_T); // type punning omitted here
    out:
        error_propagate(errp, err);
    }

v->end_struct() frees if v is an input visitor and an error occured.
The former condition is trivial, the latter requires tracking errors.
Storing &err in the stack object could do.  Alternatively, pass err
through visit_end_struct() to v->end_struct().

Yet another way to skin this cat: instead of freeing, end_struct()
returns whether to free.  Looks like this:

    out_obj:
        if (visit_end_struct(v)) {
           qapi_free_T(*obj);
        }

or maybe returns whether something was allocated:

    out_obj:
        if (visit_end_struct(v) && err) {
           qapi_free_T(*obj);
        }

These are perhaps the simplest solutions so far.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects
  2016-01-29 12:03       ` Markus Armbruster
@ 2016-01-29 15:13         ` Eric Blake
  0 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-01-29 15:13 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 2043 bytes --]

On 01/29/2016 05:03 AM, Markus Armbruster wrote:

> 
> With (1) don't assign, the caller can pick an error value by assigning
> it before the visit, and it must not access the value on error unless it
> does.
> 
> With (2) assign zero, the caller can't pick an error value, but may
> safely access the value even on error.
> 
> Tradeoff.  I figure either can work for us.
> 
>>> (3) Assign null pointer, else don't assign anything
>>>
>>>     CON: inconsistent
>>>     CON: mix of (1)'s and (2)'s CON
>>
>> Which I think is what I did in this patch.
> 
> I don't like the inconsistency.  It complicates the interface.

I'll go ahead and audit to see whether more scalar visits were relying
on (1) and would have to be rewritten to use style (2); vs. whether more
pointer visits were passing in uninitialized obj and would have to be
rewritten to use style (1).

> I think behavior (1) don't assign and (2) assign zero both work, we just
> have to pick one and run with it.
> 
> If we pick behavior (1) don't assign, then we should assert something
> like !obj || !*obj on entry.  With such assertions in place, I think (1)
> should be roughly as safe as (2).

I think your assessment is right, and it's now just a matter of seeing
which way to get to a consistent state is less effort (I may still end
up doing both ways as competing patches, for comparison purposes).


> or maybe returns whether something was allocated:
> 
>     out_obj:
>         if (visit_end_struct(v) && err) {
>            qapi_free_T(*obj);
>         }

I'm liking that.  Dealloc and output visitors always return false, and
input visitors may need to track something on their stack for whether
they allocated or returned error earlier on, but it results in less
generated output.  Basically, it's lowering the 'bool allocated' that I
added in this attempt out of the generated code and into the visitors.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit
  2016-01-22 17:12   ` Markus Armbruster
@ 2016-02-01 23:52     ` Eric Blake
  0 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-02-01 23:52 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 3001 bytes --]

On 01/22/2016 10:12 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Implement the new type_null() callback for the qmp input visitor.
>> While we don't yet have a use for this in qapi (the generator
>> will need some tweaks first), one usage is already envisioned:
>> when changing blockdev parameters, it would be nice to have a
>> difference between leaving a tuning parameter unchanged (omit
>> that parameter from the struct) and to explicitly reset the
>> parameter to its default without having to know what the default
>> value is (specify the parameter with an explicit null value,
>> which will require us to allow a qapi alternate that chooses
>> between the normal value and an explicit null).
>>
>> At any rate, we can test this without the use of generated qapi
>> by manually using visit_start_struct()/visit_end_struct().
> 
> Well, we test by calling visit_type_null() manually.  We choose to wrap
> it in a visit_start_struct() ... visit_end_struct() pair, but that's
> detail.  Actually, we do an unwrapped root visit first, and then a
> struct-wrapped visit.
> 
> Suggest "by calling visit_type_null() manually."
> 
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> ---

>> +static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
>> +{
>> +    QmpInputVisitor *qiv = to_qiv(v);
>> +    QObject *qobj = qmp_input_get_object(qiv, name, true);
>> +
>> +    if (qobject_type(qobj) == QTYPE_QNULL) {
>> +        return;
>> +    }
>> +
>> +    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>> +               "null");
> 
> Recommend to put the error in the conditional:
> 
>     if (qobject_type(qobj) != QTYPE_QNULL) {
>         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                    "null");
>     }

Sure, I can reflow the logic.

>> +    /* Check that qnull reference counting is sane:
>> +     * 1 for global use, 1 for our qnull() use, and 1 still owned by 'v'
>> +     * until it is torn down */
>> +    null = qnull();
>> +    g_assert(null->refcnt == 3);
>> +    visitor_input_teardown(data, NULL);
>> +    g_assert(null->refcnt == 2);
>> +    qobject_decref(null);
> 
> For other kinds of QObject, we leave the testing of reference counting
> to the check-qKIND.c, and don't bother with it when testing the
> visitors.  Any particular reason to do null differently?

Well, 19/37 added reference counting checks to
test-qmp-output-visitor.c, and we don't have a check-qnull.c test yet.
That, and the thing being checked here is that the visitor doesn't over-
or under-reference the static qnull object (just checking qnull()
without a visitor doesn't tell you if the visitor has any reference
counting bugs).  But maybe it is indeed worth writing a check-qnull.c
file that does this work.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions
  2016-01-22 12:18       ` Markus Armbruster
@ 2016-02-10  0:23         ` Eric Blake
  2016-02-10  7:38           ` Markus Armbruster
  0 siblings, 1 reply; 128+ messages in thread
From: Eric Blake @ 2016-02-10  0:23 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 5124 bytes --]

On 01/22/2016 05:18 AM, Markus Armbruster wrote:

> Please think twice before from growing the QAPI patch queue further.  We
> really, really need to clear it.  A TODO comment would be welcome,
> though.

Yes, especially with 2.6 soft freeze fast approaching.


>>>> +/**
>>>> + * Prepare to visit an implicit struct.
>>>> + * Similar to visit_start_struct(), except that an implicit struct
>>>> + * represents a subset of keys that are present at the same nesting level
>>>> + * of a common object but as a separate qapi C struct, rather than a new
>>>> + * object at a deeper nesting level.
>>>
>>> I'm afraid this is impenetrable.
>>>
>>> I tried to refresh my memory on visit_start_implicit_struct()'s purpose
>>> by reading code, but that's pretty impenetrable, too.
>>

>>
>> Suggestions on how to better word it are welcome.  I'm basically trying
>> to describe that this function marks the start of a new C struct used as
>> a sub-object while still conceptually parsing the same top-level QAPI
>> struct.
> 
> Thinking aloud...
> 
> QAPI defines both C data types and a QMP wire format.
> 
> The work of mapping between the two is split between generated visitor
> functions and the QMP visitors.  Roughly, the visitor functions direct
> the mapping of the tree structure, and the QMP visitors take care of the
> leaves.
> 
> The QAPI tree structure and the QMP tree structure are roughly the same.
> Exception: some structs are inlined into a parent struct in QMP, but
> stored as a separate, boxed struct in QAPI.  We call them "implicit"
> structs.  We got rid of one case (base structs), but we still got two
> (flat union and alternate members).  I dislike the exception, but we
> need to document what we've got.
> 
> It's mostly handled by the visitor functions, but two visitors need to
> hook into their handling, because they allocate / free the boxed struct:
> the QMP input visitor and the dealloc visitor.
> 
> The QMP output visitor doesn't.  The effect is that the members of the
> implicit struct get inlined into the explicit struct that surrounds it.
> 
> I oversimplified when I said "and a QMP wire format": since we acquired
> string and QemuOpts visitors, we also have a string and QemuOpts format.
> Both are partial: they don't support all of QAPI.
> 
> However, these formats aren't independent of the QMP wire format.  Since
> we use the same visitor functions, and the visitor functions map the
> tree structure, the tree structure gets mapped just like for QMP.
> Almost theoretical, because these visitors don't support most of the
> structure.
> 
> If we wanted a format that doesn't inline implicit structs, the visitor
> would want to implement visit_start_implicit_struct() and
> visit_end_implicit_struct() just like visit_start_struct() and
> visit_end_struct().  Differences:
> 
> * start_implicit_struct() lacks the @name parameter.
> 
> * end_implicit_struct() lacks the @errp now.  Later in this series, this
>   becomes: there is no check_implicit_struct().
> 
> Not inlining implicit structs seems impossible with the current API.
> I'm *not* asking for a change that makes it possible.  Instead, my point
> is: the inlining of implicit structs is baked into the visitor
> interfaces.
> 
> I'm afraid I can't turn this into a reasonable function comment without
> first amending the introduction comment to cover tree structure mapping.
> Ugh.
> 
> Crazy thought: unboxing the implicit struct should make this interface
> wart go away.  If we commit to do that later, we can "solve" our
> documentation problem the same way as for visit_start_union(): FIXME
> should not be needed.

I _want_ to get rid of the boxing.  But as it is not in my current queue
of pending patches, it will have to wait until the current queue is
flushed; so I'm going for documenting it with FIXMEs for now.

Basically, our current flat union representation is:

struct Union {
    Type tag;
    union {
        Subtype1 *one;
        Subtype2 *two;
    } u;
};

which requires two malloc's to completely populate the struct, and where
we access union->u.one->member, or pass union->u.one to a function
taking Subtype1*.  But it _should_ be:

struct Union {
    Type tag;
    union {
        Subtype1 one;
        Subtype2 two;
    } u;
};

where a single malloc is sufficient, and where we access
union->u.one.member, or pass &union->u.one to a function taking Subtype1*.

It's a tree-wide conversion; and may be easier if done in stages (fix
the generator to take a temporary boolean flag on whether a particular
structure uses inline or boxing, then a series of patches adding that
flag to a few QMP commands at a time, then a final patch to clear out
the temporary flag support) rather than all at once.  I'm not sure how
much Coccinelle will help, because I specifically haven't started the
conversion work until after we can get the current backlog flushed.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions
  2016-02-10  0:23         ` Eric Blake
@ 2016-02-10  7:38           ` Markus Armbruster
  0 siblings, 0 replies; 128+ messages in thread
From: Markus Armbruster @ 2016-02-10  7:38 UTC (permalink / raw)
  To: Eric Blake; +Cc: marcandre.lureau, qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 01/22/2016 05:18 AM, Markus Armbruster wrote:
[...]
>> Crazy thought: unboxing the implicit struct should make this interface
>> wart go away.  If we commit to do that later, we can "solve" our
>> documentation problem the same way as for visit_start_union(): FIXME
>> should not be needed.
>
> I _want_ to get rid of the boxing.  But as it is not in my current queue
> of pending patches, it will have to wait until the current queue is
> flushed; so I'm going for documenting it with FIXMEs for now.
>
> Basically, our current flat union representation is:
>
> struct Union {
>     Type tag;
>     union {
>         Subtype1 *one;
>         Subtype2 *two;
>     } u;
> };
>
> which requires two malloc's to completely populate the struct, and where
> we access union->u.one->member, or pass union->u.one to a function
> taking Subtype1*.  But it _should_ be:
>
> struct Union {
>     Type tag;
>     union {
>         Subtype1 one;
>         Subtype2 two;
>     } u;
> };
>
> where a single malloc is sufficient, and where we access
> union->u.one.member, or pass &union->u.one to a function taking Subtype1*.
>
> It's a tree-wide conversion; and may be easier if done in stages (fix
> the generator to take a temporary boolean flag on whether a particular
> structure uses inline or boxing, then a series of patches adding that
> flag to a few QMP commands at a time, then a final patch to clear out
> the temporary flag support) rather than all at once.  I'm not sure how
> much Coccinelle will help, because I specifically haven't started the
> conversion work until after we can get the current backlog flushed.

I hope the use of unions in C code is localized enough to do it in one
step.

^ permalink raw reply	[flat|nested] 128+ messages in thread

* Re: [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields
  2016-01-25 17:04   ` Markus Armbruster
@ 2016-02-17  4:57     ` Eric Blake
  0 siblings, 0 replies; 128+ messages in thread
From: Eric Blake @ 2016-02-17  4:57 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: marcandre.lureau, qemu-devel, Michael Roth

[-- Attachment #1: Type: text/plain, Size: 1230 bytes --]

On 01/25/2016 10:04 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> For empty structs, such as the 'Abort' helper type used as part
>> of the 'transaction' command, we were emitting a no-op
>> visit_type_FOO_fields().  Optimize things to instead omit calls
>> for empty structs.  Generated code changes resemble:
>>

>> Another reason for doing this optimization is that it gets us
>> closer to merging the code for visiting structs and unions:
>> since flat unions have no local members, they do not need to
>> have a visit_type_UNION_fields() emitted, even when they start
>> sharing the code used to visit structs.
>>

> 
> I'm not sure the optimization is worthwhile by itself.  Empty structs
> are rare.  I'm reserving judgement until I see the struct/union
> unification.

We managed to pull off unification without this patch, and your argument
about making the generator more verbose with no-ops if it is less
maintenance is still resonating with me.  I think I'm going to drop this
and 30/37 in my next round of patches, with no real loss in functionality.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 128+ messages in thread

end of thread, other threads:[~2016-02-17  4:57 UTC | newest]

Thread overview: 128+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-19 16:10 [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 01/37] qobject: Document more shortcomings in our number handling Eric Blake
2016-01-20  9:02   ` Markus Armbruster
2016-01-20 15:55     ` Eric Blake
2016-01-21  6:21       ` Markus Armbruster
2016-01-21 17:12         ` Eric Blake
2016-01-21 17:29         ` Daniel P. Berrange
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 02/37] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
2016-01-20 10:04   ` Markus Armbruster
2016-01-20 15:59     ` Eric Blake
2016-01-21  6:22       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 03/37] qapi: Drop dead dealloc visitor variable Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 04/37] hmp: Improve use of qapi visitor Eric Blake
2016-01-20 13:05   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 05/37] vl: " Eric Blake
2016-01-20 13:43   ` Markus Armbruster
2016-01-20 16:18     ` Eric Blake
2016-01-21  6:45       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 06/37] balloon: " Eric Blake
2016-01-20 14:09   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 07/37] qapi: Improve generated event " Eric Blake
2016-01-20 15:19   ` Markus Armbruster
2016-01-20 17:10     ` Eric Blake
2016-01-21  7:16       ` Markus Armbruster
2016-01-26 23:40       ` Eric Blake
2016-01-28 22:51     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 08/37] qapi: Track all failures between visit_start/stop Eric Blake
2016-01-20 16:03   ` Markus Armbruster
2016-01-20 17:15     ` Eric Blake
2016-01-21  7:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 09/37] qapi: Prefer type_int64 over type_int in visitors Eric Blake
2016-01-20 17:07   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 10/37] qapi: Make all visitors supply uint64 callbacks Eric Blake
2016-01-20 17:29   ` Markus Armbruster
2016-01-20 18:10     ` Eric Blake
2016-01-21  8:56       ` Markus Armbruster
2016-01-21 17:22         ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 11/37] qapi: Consolidate visitor small integer callbacks Eric Blake
2016-01-20 17:34   ` Markus Armbruster
2016-01-20 18:17     ` Eric Blake
2016-01-21  9:05       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 12/37] qapi: Don't cast Enum* to int* Eric Blake
2016-01-20 18:08   ` Markus Armbruster
2016-01-20 19:58     ` Eric Blake
2016-01-21  9:08       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 13/37] qom: Use typedef for Visitor Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 14/37] qapi: Swap visit_* arguments for consistent 'name' placement Eric Blake
2016-01-20 18:28   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 15/37] qom: Swap 'name' next to visitor in ObjectPropertyAccessor Eric Blake
2016-01-20 18:49   ` Markus Armbruster
2016-01-20 20:54     ` Eric Blake
2016-01-21  9:18       ` Markus Armbruster
2016-01-21  9:46         ` Kevin Wolf
2016-01-21 10:04           ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 16/37] qapi: Swap 'name' in visit_* callbacks to match public API Eric Blake
2016-01-20 18:55   ` Markus Armbruster
2016-01-20 21:01     ` Eric Blake
2016-01-21  9:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 17/37] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
2016-01-20 18:59   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 18/37] qapi: Drop unused error argument for list and implicit struct Eric Blake
2016-01-20 19:03   ` Markus Armbruster
2016-01-20 21:58     ` Eric Blake
2016-01-21  9:47       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 19/37] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
2016-01-21 10:27   ` Markus Armbruster
2016-01-21 13:19     ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 20/37] qmp: Don't abuse stack to track qmp-output root Eric Blake
2016-01-21 13:58   ` Markus Armbruster
2016-01-29  3:06     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 21/37] qapi: Document visitor interfaces, add assertions Eric Blake
2016-01-21 20:08   ` Markus Armbruster
2016-01-22  0:30     ` Eric Blake
2016-01-22 12:18       ` Markus Armbruster
2016-02-10  0:23         ` Eric Blake
2016-02-10  7:38           ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 22/37] qapi: Add visit_type_null() visitor Eric Blake
2016-01-22 17:00   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 23/37] qmp: Support explicit null during input visit Eric Blake
2016-01-22 17:12   ` Markus Armbruster
2016-02-01 23:52     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 24/37] qmp: Tighten output visitor rules Eric Blake
2016-01-22 19:11   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 25/37] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
2016-01-22 19:15   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 26/37] qapi: Simplify excess input reporting in input visitors Eric Blake
2016-01-22 19:24   ` Markus Armbruster
2016-01-22 19:37     ` Eric Blake
2016-01-25  9:27       ` Markus Armbruster
2016-01-25 10:43         ` Laszlo Ersek
2016-01-27 13:54   ` [Qemu-devel] [PATCH 0/3] qapi-visit: Unify struct and union visit Markus Armbruster
2016-01-27 13:54     ` [Qemu-devel] [PATCH 1/3] qapi-visit: Simplify how we visit common union members Markus Armbruster
2016-01-27 21:48       ` Eric Blake
2016-01-27 13:54     ` [Qemu-devel] [PATCH 2/3] qapi-visit: Clean up code generated around visit_end_union() Markus Armbruster
2016-01-27 14:02       ` Eric Blake
2016-01-27 14:46         ` Markus Armbruster
2016-01-27 13:54     ` [Qemu-devel] [PATCH 3/3] qapi-visit: Unify struct and union visit Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 27/37] qapi: Add type.is_empty() helper Eric Blake
2016-01-25 14:15   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 28/37] qapi: Fix command with named empty argument type Eric Blake
2016-01-25 15:03   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 29/37] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
2016-01-25 17:04   ` Markus Armbruster
2016-02-17  4:57     ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 30/37] qapi: Canonicalize missing object to :empty Eric Blake
2016-01-25 19:04   ` Markus Armbruster
2016-01-26 16:29     ` Markus Armbruster
2016-01-27  8:00       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 31/37] qapi-visit: Unify struct and union visit Eric Blake
2016-01-27 14:12   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 32/37] qapi: Rework deallocation of partial struct Eric Blake
2016-01-27 16:41   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 33/37] qapi: Split visit_end_struct() into pieces Eric Blake
2016-01-27 17:20   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 34/37] qapi: Simplify semantics of visit_next_list() Eric Blake
2016-01-28 13:37   ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 35/37] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
2016-01-28 15:24   ` Markus Armbruster
2016-01-28 17:05     ` Eric Blake
2016-01-29 12:03       ` Markus Armbruster
2016-01-29 15:13         ` Eric Blake
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 36/37] RFC: qapi: Adjust layout of FooList types Eric Blake
2016-01-28 15:34   ` Markus Armbruster
2016-01-28 17:23     ` Eric Blake
2016-01-29  8:19       ` Markus Armbruster
2016-01-19 16:10 ` [Qemu-devel] [PATCH v9 37/37] qapi: Update docs to match recent generator changes Eric Blake
2016-01-28 15:37   ` Markus Armbruster
2016-01-28 17:56 ` [Qemu-devel] [PATCH v9 00/37] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster

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.