All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E)
@ 2015-12-08  3:54 Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 01/31] qobject: Document more shortcomings in our number handling Eric Blake
                   ` (30 more replies)
  0 siblings, 31 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

Pending prerequisites:
+ Markus' qapi-not-next branch (including my subset D patches)
http://repo.or.cz/qemu/armbru.git/shortlog/refs/heads/qapi-not-next
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg00463.html

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

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

v7 notes:
Rearrange several patches (trying to float the obvious ones up
front, moving hunks between patches to make review easier), and
add several new patches.  In particular, I'm quite pleased with
how 29/31 turned out as a new patch for simplifying visitor
semantics; and 18-20 address an issue that caused some last-minute
flurry before 2.5-rc3.

Patch 31 is marked RFC because it might be overkill; when I first
conceived it, I thought a simple change to qapi-types.py and
GenericList in visitor.h would be enough to save memory; until
valgrind reminded me that if sizeof(GenericList) shrinks and
becomes variably-sized, I have to start passing sizes around.  The
additional changes to visit_start_list() and visit_next_list()
didn't turn out as lean as I had hoped, so I'm okay if we ditch
that patch.

I'm very seriously considering writing a new JSON output visitor,
so that we can go straight from qapi to JSON without having to go
through an intermediate QObject tree, and as proof that the visitor
interface is starting to be better documented.  But I guess I should
first focus on getting my subset F patches ready to go (we still
want to get netdev_add introspectible), since those have at least
been on list before.

Some patch retitling makes the backport-diff claim more new patches
than there actually are.

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

v6 notes:
https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg05793.html
My set of patches related to qapi visitors has grown, and it's time
that I post it on list again.  Of course, since this is all 2.6
material, and there's already lots of patches earlier in the queue,
I may need a v7 to pick up rebase changes.

A lot of the new patches in this series are based on fallout from
implementing an early RFC posted against a v5 review:
https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg06878.html

v5 and earlier - look in the mail archives :)

Eric Blake (31):
  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*
  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: 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

 hmp.c                                   |  19 +-
 hw/core/qdev-properties.c               |   6 +-
 hw/ppc/spapr_drc.c                      |  19 +-
 hw/virtio/virtio-balloon.c              |  25 +--
 include/qapi/qmp-output-visitor.h       |   1 +
 include/qapi/visitor-impl.h             |  92 ++++++----
 include/qapi/visitor.h                  | 281 ++++++++++++++++++++++++++---
 qapi/opts-visitor.c                     | 111 +++++++-----
 qapi/qapi-dealloc-visitor.c             |  97 ++++------
 qapi/qapi-visit-core.c                  | 309 +++++++++++++++++---------------
 qapi/qmp-input-visitor.c                | 158 ++++++++--------
 qapi/qmp-output-visitor.c               | 149 +++++++--------
 qapi/string-input-visitor.c             |  83 +++++----
 qapi/string-output-visitor.c            |  78 ++++----
 qobject/json-parser.c                   |   4 +-
 qobject/qjson.c                         |   8 +-
 qom/object.c                            |  13 +-
 scripts/qapi-commands.py                |   7 +-
 scripts/qapi-event.py                   |  24 +--
 scripts/qapi-types.py                   |  11 +-
 scripts/qapi-visit.py                   | 248 +++++++++++++------------
 scripts/qapi.py                         |  30 ++--
 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-qmp-commands.c               |  18 +-
 tests/test-qmp-input-strict.c           |  19 +-
 tests/test-qmp-input-visitor.c          |  10 +-
 tests/test-qmp-output-visitor.c         |   4 +
 vl.c                                    |  29 +--
 35 files changed, 1129 insertions(+), 784 deletions(-)

-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 01/31] qobject: Document more shortcomings in our number handling
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 02/31] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
                   ` (29 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

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

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

* [Qemu-devel] [PATCH v7 02/31] qapi: Avoid use of misnamed DO_UPCAST()
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 01/31] qobject: Document more shortcomings in our number handling Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 03/31] qapi: Drop dead dealloc visitor variable Eric Blake
                   ` (28 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

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

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

* [Qemu-devel] [PATCH v7 03/31] qapi: Drop dead dealloc visitor variable
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 01/31] qobject: Document more shortcomings in our number handling Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 02/31] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 04/31] hmp: Improve use of qapi visitor Eric Blake
                   ` (27 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

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

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

* [Qemu-devel] [PATCH v7 04/31] hmp: Improve use of qapi visitor
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (2 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 03/31] qapi: Drop dead dealloc visitor variable Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 05/31] vl: " Eric Blake
                   ` (26 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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 c2b2c16..0d21f1d 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1667,9 +1667,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) {
@@ -1678,28 +1678,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);
     }
@@ -1711,7 +1712,6 @@ out_clean:
     qemu_opts_del(opts);
     g_free(id);
     g_free(type);
-    g_free(dummy);

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

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

* [Qemu-devel] [PATCH v7 05/31] vl: Improve use of qapi visitor
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (3 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 04/31] hmp: Improve use of qapi visitor Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 06/31] balloon: " Eric Blake
                   ` (25 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, armbru

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>

---
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 5aaea77..11555ac 100644
--- a/vl.c
+++ b/vl.c
@@ -2828,44 +2828,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);
@@ -2873,7 +2876,6 @@ out:
     QDECREF(pdict);
     g_free(id);
     g_free(type);
-    g_free(dummy);
     if (err) {
         error_report_err(err);
         return -1;
-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 06/31] balloon: Improve use of qapi visitor
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (4 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 05/31] vl: " Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 07/31] qapi: Improve generated event " Eric Blake
                   ` (24 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

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

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

* [Qemu-devel] [PATCH v7 07/31] qapi: Improve generated event use of qapi visitor
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (5 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 06/31] balloon: " Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 08/31] qapi: Track all failures between visit_start/stop Eric Blake
                   ` (23 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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, rearrange the declaration to
make it easier for a future patch to introduce the notion of
a boxed event visit, 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>

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

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 720486f..e37c07a 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -41,9 +41,9 @@ def gen_event_send(name, arg_type):

     if arg_type and arg_type.members:
         ret += mcgen('''
+    QObject *obj;
     QmpOutputVisitor *qov;
     Visitor *v;
-    QObject *obj;

 ''')

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

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

* [Qemu-devel] [PATCH v7 08/31] qapi: Track all failures between visit_start/stop
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (6 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 07/31] qapi: Improve generated event " Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 09/31] qapi: Prefer type_int64 over type_int in visitors Eric Blake
                   ` (22 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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.

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

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

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

* [Qemu-devel] [PATCH v7 09/31] qapi: Prefer type_int64 over type_int in visitors
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (7 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 08/31] qapi: Track all failures between visit_start/stop Eric Blake
@ 2015-12-08  3:54 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 10/31] qapi: Make all visitors supply uint64 callbacks Eric Blake
                   ` (21 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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).

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

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

---
v7: split off of 1/23 and 2/23 for easier-to-read diffs
---
 include/qapi/visitor-impl.h  |  1 -
 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, 39 insertions(+), 32 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 44a21b7..70326e0 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -36,7 +36,6 @@ 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);
     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,
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.4.3

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

* [Qemu-devel] [PATCH v7 10/31] qapi: Make all visitors supply uint64 callbacks
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (8 preceding siblings ...)
  2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 09/31] qapi: Prefer type_int64 over type_int in visitors Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 11/31] qapi: Consolidate visitor small integer callbacks Eric Blake
                   ` (20 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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
---
 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 +++++++++
 6 files changed, 70 insertions(+), 34 deletions(-)

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

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

* [Qemu-devel] [PATCH v7 11/31] qapi: Consolidate visitor small integer callbacks
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (9 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 10/31] qapi: Make all visitors supply uint64 callbacks Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 12/31] qapi: Don't cast Enum* to int* Eric Blake
                   ` (19 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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 |  22 +++---
 qapi/qapi-visit-core.c      | 158 +++++++++++++++++---------------------------
 2 files changed, 70 insertions(+), 110 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 70326e0..5ee2974 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 Red Hat, Inc.
  *
  * Author: Paolo Bonizni <pbonzini@redhat.com>
  *
@@ -36,6 +36,16 @@ struct Visitor
     void (*get_next_type)(Visitor *v, QType *type, bool promote_int,
                           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_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,
@@ -46,16 +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_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);
-    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);
     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.4.3

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

* [Qemu-devel] [PATCH v7 12/31] qapi: Don't cast Enum* to int*
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (10 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 11/31] qapi: Consolidate visitor small integer callbacks Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
                   ` (18 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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
"qapi: Simplify visiting of alternate types".

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

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

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

* [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (11 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 12/31] qapi: Don't cast Enum* to int* Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  4:40   ` David Gibson
  2015-12-11 13:51   ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 14/31] qapi: Drop unused error argument for list and implicit struct Eric Blake
                   ` (17 subsequent siblings)
  30 siblings, 2 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael S. Tsirkin, armbru, Michael Roth, Alexander Graf,
	open list:sPAPR pseries, Paolo Bonzini, 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.  While at it, change
the signature of visit_start_struct() to place the 'name'
argument at the end (other than 'errp'), and the 'size' argument
next to 'obj'; this placement of 'name' matches matches how all
other functions in visit.h do it (visit_type_enum() places
'strings' between 'obj' and 'name'; visit_get_next_type() places
'promote_int' between 'type' and 'name').  This also avoids the
confusion caused by splitting related pieces of information,
where the old signature an unrelated parameter in between the
"typename" and sizeof(typename) arguments.

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

---
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 | 16 ++++++++--------
 include/qapi/visitor.h      |  8 ++++----
 qapi/opts-visitor.c         |  4 ++--
 qapi/qapi-dealloc-visitor.c | 10 ++++------
 qapi/qapi-visit-core.c      | 23 +++++++++++------------
 qapi/qmp-input-visitor.c    |  4 ++--
 qapi/qmp-output-visitor.c   |  5 ++---
 qom/object.c                |  8 ++++----
 scripts/qapi-event.py       |  2 +-
 scripts/qapi-visit.py       | 12 ++++++------
 vl.c                        |  2 +-
 15 files changed, 52 insertions(+), 58 deletions(-)

diff --git a/hmp.c b/hmp.c
index 0d21f1d..f81f332 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1680,7 +1680,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, 0, NULL, &err);
     if (err) {
         goto out_clean;
     }
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 33e245e..d81d689 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, 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, ptr, prop->info->enum_table, prop->name, errp);
 }

 static void set_enum(Object *obj, Visitor *v, void *opaque,
@@ -64,8 +63,7 @@ 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, ptr, prop->info->enum_table, prop->name, errp);
 }

 /* Bit */
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 8be62c3..96d06f5 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, void *opaque,
     void *fdt;

     if (!drc->fdt) {
-        visit_start_struct(v, NULL, NULL, name, 0, &err);
+        visit_start_struct(v, NULL, 0, name, &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, NULL, 0, name, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 1ce987a..cb8237f 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, struct Visitor *v,
     VirtIOBalloon *s = opaque;
     int i;

-    visit_start_struct(v, NULL, "guest-stats", name, 0, &err);
+    visit_start_struct(v, NULL, 0, name, &err);
     if (err) {
         goto out;
     }
@@ -126,7 +126,7 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v,
         goto out_end;
     }

-    visit_start_struct(v, NULL, NULL, "stats", 0, &err);
+    visit_start_struct(v, NULL, 0, "stats", &err);
     if (err) {
         goto out_end;
     }
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 5ee2974..6737005 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, void **obj, size_t size,
+                         const char *name, Error **errp);
     void (*end_struct)(Visitor *v, Error **errp);

     void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
@@ -30,8 +30,8 @@ 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, int *obj, const char *const strings[],
+                      const char *name, 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);
@@ -60,9 +60,9 @@ struct Visitor
     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, int *obj, const char *const strings[],
+                     const char *name, Error **errp);
+void output_type_enum(Visitor *v, int *obj, const char *const strings[],
+                      const char *name, Error **errp);

 #endif
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index a14a16d..21891ca 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, void **obj, size_t size,
+                        const char *name, Error **errp);
 void visit_end_struct(Visitor *v, Error **errp);
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
@@ -53,8 +53,8 @@ bool visit_optional(Visitor *v, bool *present, const char *name);
  */
 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_enum(Visitor *v, int *obj, const char *const strings[],
+                     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);
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 56c798f..df5b537 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, void **obj, size_t size,
+                  const char *name, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     const QemuOpt *opt;
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 11eb828..f4c0a67 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -58,9 +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,
-                                      Error **errp)
+static void qapi_dealloc_start_struct(Visitor *v, void **obj, size_t unused,
+                                      const char *name, Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     qapi_dealloc_push(qov, obj);
@@ -164,9 +163,8 @@ static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
 }

 static void qapi_dealloc_type_enum(Visitor *v, int *obj,
-                                   const char * const strings[],
-                                   const char *kind, const char *name,
-                                   Error **errp)
+                                   const char *const strings[],
+                                   const char *name, Error **errp)
 {
 }

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index a48fd4e..d9ec1cc 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>
@@ -17,10 +18,10 @@
 #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, void **obj, size_t size,
+                        const char *name, Error **errp)
 {
-    v->start_struct(v, obj, kind, name, size, errp);
+    v->start_struct(v, obj, size, name, errp);
 }

 void visit_end_struct(Visitor *v, Error **errp)
@@ -89,10 +90,10 @@ void visit_get_next_type(Visitor *v, QType *type, bool promote_int,
     }
 }

-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, int *obj, const char *const strings[],
+                     const char *name, Error **errp)
 {
-    v->type_enum(v, obj, strings, kind, name, errp);
+    v->type_enum(v, obj, strings, name, errp);
 }

 void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
@@ -220,9 +221,8 @@ void visit_type_any(Visitor *v, QObject **obj, const char *name,
     v->type_any(v, obj, name, errp);
 }

-void output_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 *name, Error **errp)
 {
     int i = 0;
     int value = *obj;
@@ -239,9 +239,8 @@ void output_type_enum(Visitor *v, int *obj, const char * const strings[],
     visit_type_str(v, &enum_str, name, errp);
 }

-void input_type_enum(Visitor *v, int *obj, const char * const strings[],
-                     const char *kind, const char *name,
-                     Error **errp)
+void input_type_enum(Visitor *v, int *obj, const char *const strings[],
+                     const char *name, 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 32b60bb..45ddce7 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, void **obj, size_t size,
+                                   const char *name, 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 f8eebaa..8cb82cb 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -110,9 +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,
-                                    Error **errp)
+static void qmp_output_start_struct(Visitor *v, void **obj, size_t unused,
+                                    const char *name, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QDict *dict = qdict_new();
diff --git a/qom/object.c b/qom/object.c
index d751569..5eb65ea 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1186,7 +1186,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),
-                    &ret, enumprop->strings, NULL, name, errp);
+                    &ret, enumprop->strings, name, errp);

     g_free(str);
     string_input_visitor_cleanup(siv);
@@ -1810,7 +1810,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, &value, prop->strings, name, errp);
 }

 static void property_set_enum(Object *obj, Visitor *v, void *opaque,
@@ -1820,7 +1820,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, &value, prop->strings, name, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -1876,7 +1876,7 @@ 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, NULL, 0, name, &err);
     if (err) {
         goto out;
     }
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index e37c07a..111958d 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, NULL, 0, "%(name)s", &err);
 ''',
                      name=name)
         ret += gen_err_check()
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 6bd188b..1a86afd 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -122,7 +122,7 @@ 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);
+    visit_start_struct(v, (void **)obj, sizeof(%(c_name)s), name, &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, %(c_name)s *obj, const char *name, Error **errp)
 {
     int tmp = *obj;
-    visit_type_enum(v, &tmp, %(c_name)s_lookup, "%(name)s", name, errp);
+    visit_type_enum(v, &tmp, %(c_name)s_lookup, name, 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, %(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);
+    visit_start_struct(v, (void **)obj, sizeof(%(c_name)s), name, &err);
     if (err) {
         goto out;
     }
@@ -267,7 +267,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, 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 11555ac..1010bdb 100644
--- a/vl.c
+++ b/vl.c
@@ -2840,7 +2840,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, 0, NULL, &err);
     if (err) {
         goto out;
     }
-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 14/31] qapi: Drop unused error argument for list and implicit struct
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (12 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  4:40   ` David Gibson
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 15/31] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
                   ` (16 subsequent siblings)
  30 siblings, 1 reply; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, Alexander Graf, open list:sPAPR pseries, armbru,
	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.

A later patch will then tackle the larger task of splitting
visit_end_struct(), which can indeed set an error.

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

---
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 96d06f5..dcce563 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, void *opaque,
                     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 6737005..c3c6323 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, int *obj, const char *const strings[],
                       const char *name, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 21891ca..21c09b4 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -32,10 +32,11 @@ void visit_start_struct(Visitor *v, void **obj, size_t size,
 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 df5b537..e8c7517 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 f4c0a67..e280d9f 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 d9ec1cc..3bd5cae 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 45ddce7..a7ebbb6 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, QType *type, bool promote_int,
@@ -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 8cb82cb..4bd2ad0 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 d7546b5..0b0bb6e 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 3ed2b2c..13b00c7 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 1a86afd..acb237b 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, %(c_name)s **obj, const char *name, Error
         visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &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, %(c_name)s **obj, const char *name, 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.4.3

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

* [Qemu-devel] [PATCH v7 15/31] qmp: Fix reference-counting of qnull on empty output visit
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (13 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 14/31] qapi: Drop unused error argument for list and implicit struct Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 16/31] qmp: Don't abuse stack to track qmp-output root Eric Blake
                   ` (15 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: Michael Roth, 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.

Signed-off-by: Eric Blake <eblake@redhat.com>
Cc: qemu-stable@nongnu.org

---
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 4bd2ad0..77097b7 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, QObject **obj, const char *name,
     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 3078442..8e6fc33 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.4.3

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

* [Qemu-devel] [PATCH v7 16/31] qmp: Don't abuse stack to track qmp-output root
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (14 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 15/31] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 17/31] qapi: Document visitor interfaces, add assertions Eric Blake
                   ` (14 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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 77097b7..06ee19b 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, QObject **obj, const char *name,
 /* 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.4.3

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

* [Qemu-devel] [PATCH v7 17/31] qapi: Document visitor interfaces, add assertions
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (15 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 16/31] qmp: Don't abuse stack to track qmp-output root Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 18/31] qapi: Add visit_type_null() visitor Eric Blake
                   ` (13 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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 assertions; 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>

---
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      | 223 ++++++++++++++++++++++++++++++++++++++++++--
 qapi/qapi-visit-core.c      |  39 +++++++-
 3 files changed, 280 insertions(+), 13 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index c3c6323..27f776f 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, void **obj, size_t size,
                          const char *name, 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, int *obj, const char *const strings[],
                       const char *name, 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, uint64_t *obj, const char *name,
                       Error **errp);
+
     /* Must be set. */
     void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
+    /* Must be set */
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
+
+    /* Must be provided to visit numbers (the opts visitor does not
+     * currently visit non-integers). */
     void (*type_number)(Visitor *v, double *obj, const char *name,
                         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, QObject **obj, const char *name,
                      Error **errp);

     /* May be NULL; most useful for input visitors. */
     void (*optional)(Visitor *v, bool *present, const char *name);

+    /* 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, int *obj, const char *const strings[],
                      const char *name, Error **errp);
+/**
+ * A generic visitor.type_enum suitable for output visitors.
+ */
 void output_type_enum(Visitor *v, int *obj, const char *const strings[],
                       const char *name, Error **errp);

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 21c09b4..96a9535 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, void **obj, size_t size,
                         const char *name, 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,23 +154,128 @@ bool visit_optional(Visitor *v, bool *present, const char *name);
  */
 void visit_get_next_type(Visitor *v, QType *type, bool promote_int,
                          const char *name, 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, int *obj, const char *const strings[],
                      const char *name, 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, 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);
+/**
+ * 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, uint8_t *obj, const char *name,
+                      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, uint16_t *obj, const char *name,
+                       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, uint32_t *obj, const char *name,
+                       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, uint64_t *obj, const char *name,
+                       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, 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);
+/**
+ * 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, int16_t *obj, const char *name,
+                      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, int32_t *obj, const char *name,
+                      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, int64_t *obj, const char *name,
+                      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, uint64_t *obj, const char *name,
+                     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, bool *obj, const char *name, 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, char **obj, const char *name, Error **errp);
-void visit_type_number(Visitor *v, double *obj, const char *name, 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, double *obj, const char *name,
+                       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, QObject **obj, 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-visit-core.c b/qapi/qapi-visit-core.c
index 3bd5cae..042ef07 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, void **obj, size_t size,
                         const char *name, Error **errp)
 {
+    assert(obj ? size : !size);
+    if (obj && visit_is_output(v)) {
+        assert(*obj);
+    }
     v->start_struct(v, obj, size, name, 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, bool *present, const char *name)
 void visit_get_next_type(Visitor *v, QType *type, bool promote_int,
                          const char *name, Error **errp)
 {
+    assert(type);
     if (v->get_next_type) {
         v->get_next_type(v, type, promote_int, name, errp);
     }
@@ -93,11 +111,13 @@ void visit_get_next_type(Visitor *v, QType *type, bool promote_int,
 void visit_type_enum(Visitor *v, int *obj, const char *const strings[],
                      const char *name, Error **errp)
 {
+    assert(obj && strings);
     v->type_enum(v, obj, strings, name, errp);
 }

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

@@ -144,6 +164,7 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name,
 void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name,
                        Error **errp)
 {
+    assert(obj);
     v->type_uint64(v, obj, name, errp);
 }

@@ -188,11 +209,13 @@ 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)
 {
+    assert(obj);
     v->type_int64(v, obj, name, errp);
 }

 void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
+    assert(obj);
     if (v->type_size) {
         v->type_size(v, obj, name, errp);
     } else {
@@ -202,22 +225,35 @@ 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)
 {
+    assert(obj);
     v->type_bool(v, obj, name, errp);
 }

 void visit_type_str(Visitor *v, char **obj, const char *name, 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, obj, name, errp);
 }

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

 void visit_type_any(Visitor *v, QObject **obj, const char *name,
                     Error **errp)
 {
+    assert(obj);
+    if (visit_is_output(v)) {
+        assert(*obj);
+    }
     v->type_any(v, obj, name, errp);
 }

@@ -228,7 +264,6 @@ void output_type_enum(Visitor *v, int *obj, const char *const strings[],
     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");
@@ -246,8 +281,6 @@ void input_type_enum(Visitor *v, int *obj, const char *const strings[],
     int64_t value = 0;
     char *enum_str;

-    assert(strings);
-
     visit_type_str(v, &enum_str, name, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 18/31] qapi: Add visit_type_null() visitor
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (16 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 17/31] qapi: Document visitor interfaces, add assertions Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 19/31] qmp: Tighten output visitor rules Eric Blake
                   ` (12 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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
a later patch will then wire it up into the qmp output visitor.

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

---
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 27f776f..78a5ab1 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, QObject **obj, const char *name,
                      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, bool *present, const char *name);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 96a9535..2592dd1 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -267,6 +267,14 @@ void visit_type_number(Visitor *v, double *obj, const char *name,
 void visit_type_any(Visitor *v, QObject **obj, const char *name, 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 e280d9f..e3a4493 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, QObject **obj,
     }
 }

+static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
+{
+}
+
 static void qapi_dealloc_type_enum(Visitor *v, int *obj,
                                    const char *const strings[],
                                    const char *name, Error **errp)
@@ -223,6 +227,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 042ef07..36917f3 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -257,6 +257,11 @@ void visit_type_any(Visitor *v, QObject **obj, const char *name,
     v->type_any(v, obj, name, errp);
 }

+void visit_type_null(Visitor *v, const char *name, Error **errp)
+{
+    v->type_null(v, name, errp);
+}
+
 void output_type_enum(Visitor *v, int *obj, const char *const strings[],
                       const char *name, Error **errp)
 {
-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 19/31] qmp: Tighten output visitor rules
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (17 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 18/31] qapi: Add visit_type_null() visitor Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 20/31] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
                   ` (11 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Add a new qmp_output_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 (so
that the accidental return of NULL fixed by commit ab8bf1d7 will
be much easier to diagnose), and that it may only be called once
per visit.

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>

---
v7: new patch, based on discussion about spapr_drc.c
---
 include/qapi/qmp-output-visitor.h |  1 +
 include/qapi/visitor-impl.h       |  2 +-
 qapi/qmp-output-visitor.c         | 37 ++++++++++++++++++++++++-------------
 tests/test-qmp-output-visitor.c   |  2 ++
 4 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
index 2266770..184195b 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_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 78a5ab1..628cab7 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -76,7 +76,7 @@ struct Visitor
     void (*type_any)(Visitor *v, QObject **obj, const char *name,
                      Error **errp);
     /* Must be provided to visit explicit null values (right now, only the
-     * dealloc visitor supports this).  */
+     * dealloc and qmp-output 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-output-visitor.c b/qapi/qmp-output-visitor.c
index 06ee19b..28f9854 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 Red Hat, Inc.
  * Copyright IBM, Corp. 2011
  *
  * Authors:
@@ -88,9 +89,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)) {
@@ -202,18 +202,22 @@ static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
     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 +225,7 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
     return &v->visitor;
 }

-void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+void qmp_output_reset(QmpOutputVisitor *v)
 {
     QStackEntry *e, *tmp;

@@ -231,6 +235,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
     }

     qobject_decref(v->root);
+    v->root = NULL;
+}
+
+void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
+{
+    qmp_output_reset(v);
     g_free(v);
 }

@@ -252,6 +262,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 8e6fc33..53d1372 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, &pu, "unused", &err);
         g_assert(err);
         error_free(err);
+        qmp_output_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.4.3

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

* [Qemu-devel] [PATCH v7 20/31] spapr_drc: Expose 'null' in qom-get when there is no fdt
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (18 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 19/31] qmp: Tighten output visitor rules Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  4:40   ` David Gibson
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 21/31] qapi: Simplify excess input reporting in input visitors Eric Blake
                   ` (10 subsequent siblings)
  30 siblings, 1 reply; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alexander Graf, open list:sPAPR pseries, 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 1d10b44; but that this time,
the change is intentional and not an accidental side-effect.)

Signed-off-by: Eric Blake <eblake@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>

---
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 dcce563..0c675ff 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, void *opaque,
     void *fdt;

     if (!drc->fdt) {
-        visit_start_struct(v, NULL, 0, name, &err);
-        if (!err) {
-            visit_end_struct(v, &err);
-        }
-        error_propagate(errp, err);
+        visit_type_null(v, NULL, errp);
         return;
     }

-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 21/31] qapi: Simplify excess input reporting in input visitors
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (19 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 20/31] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 22/31] qapi: Add type.is_empty() helper Eric Blake
                   ` (9 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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 e8c7517..a767391 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -156,17 +156,11 @@ opts_start_struct(Visitor *v, void **obj, size_t size,
 }


-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 a7ebbb6..0587b8e 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.4.3

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

* [Qemu-devel] [PATCH v7 22/31] qapi: Add type.is_empty() helper
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (20 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 21/31] qapi: Simplify excess input reporting in input visitors Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 23/31] qapi: Fix command with named empty argument type Eric Blake
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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.

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

---
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 111958d..f2feaaf 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('''
     QObject *obj;
     QmpOutputVisitor *qov;
@@ -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 0d86269..8f57200 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 497eaba..8b388ee 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.4.3

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

* [Qemu-devel] [PATCH v7 23/31] qapi: Fix command with named empty argument type
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (21 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 22/31] qapi: Add type.is_empty() helper Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 24/31] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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 561e47a..38cbffc 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 9f35b80..b132775 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.4.3

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

* [Qemu-devel] [PATCH v7 24/31] qapi: Eliminate empty visit_type_FOO_fields
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (22 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 23/31] qapi: Fix command with named empty argument type Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 25/31] qapi: Canonicalize missing object to :empty Eric Blake
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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, Abort **obj, const char *name, 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>

---
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 acb237b..4ec0d22 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -35,22 +35,22 @@ void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, 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, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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.4.3

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

* [Qemu-devel] [PATCH v7 25/31] qapi: Canonicalize missing object to :empty
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (23 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 24/31] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 26/31] qapi-visit: Unify struct and union visit Eric Blake
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
v7: rebase to earlier changes
v6: new patch
---
 scripts/qapi-commands.py                |  7 ++---
 scripts/qapi-event.py                   |  5 ++--
 scripts/qapi-types.py                   |  4 +--
 scripts/qapi-visit.py                   | 12 +++++----
 scripts/qapi.py                         | 22 ++++++++--------
 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, 77 insertions(+), 29 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 38cbffc..0f3cc57 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -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 f2feaaf..6a29b6c 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('''
     QObject *obj;
     QmpOutputVisitor *qov;
@@ -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 8f57200..91c5ae0 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 4ec0d22..2f71dac 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, %(c_name)s **obj, const char *name, 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 8b388ee..814cb05 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))
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.4.3

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

* [Qemu-devel] [PATCH v7 26/31] qapi-visit: Unify struct and union visit
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (24 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 25/31] qapi: Canonicalize missing object to :empty Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 27/31] qapi: Rework deallocation of partial struct Eric Blake
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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, &(*obj)->type, "type", &err);
|+    if (err) {
|+        goto out;
|+    }
|+
|+out:
|+    error_propagate(errp, err);
|+}
|+
| void visit_type_ChardevBackend(Visitor *v, ChardevBackend **obj, const char *name, Error **errp)
| {
|     Error *err = NULL;
|@@ -2319,7 +2332,7 @@ void visit_type_ChardevBackend(Visitor *
|     if (!*obj) {
|         goto out_obj;
|     }
|-    visit_type_ChardevBackendKind(v, &(*obj)->type, "type", &err);
|+    visit_type_ChardevBackend_fields(v, obj, &err);
|     if (err) {

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

---
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 2f71dac..e045017 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, %(c_name)s **obj, const char *name, Error **errp)
-{
-    Error *err = NULL;
-
-    visit_start_struct(v, (void **)obj, sizeof(%(c_name)s), name, &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, %(c_name)s **obj, const char *name, Error **errp)
@@ -272,61 +238,71 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, 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, &(*obj)->%(c_name)s, "%(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, &(*obj)->u.%(c_name)s, "data", &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.4.3

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

* [Qemu-devel] [PATCH v7 27/31] qapi: Rework deallocation of partial struct
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (25 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 26/31] qapi-visit: Unify struct and union visit Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 28/31] qapi: Split visit_end_struct() into pieces Eric Blake
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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 628cab7..3b67dc7 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, bool *present, const char *name);
-
-    /* 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 2592dd1..b8146d7 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -274,16 +274,4 @@ void visit_type_any(Visitor *v, QObject **obj, const char *name, 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 e3a4493..92345aa 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -172,31 +172,6 @@ static void qapi_dealloc_type_enum(Visitor *v, 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;
@@ -228,7 +203,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 36917f3..09cc5c9 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, bool *present, const char *name)
 {
     if (v->optional) {
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index e045017..5c8f1e6 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, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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.4.3

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

* [Qemu-devel] [PATCH v7 28/31] qapi: Split visit_end_struct() into pieces
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (26 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 27/31] qapi: Rework deallocation of partial struct Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  4:42   ` David Gibson
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list() Eric Blake
                   ` (2 subsequent siblings)
  30 siblings, 1 reply; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael S. Tsirkin, armbru, Michael Roth, Alexander Graf,
	open list:sPAPR pseries, Paolo Bonzini, 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>

---
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 ++++-----
 vl.c                        | 11 +++++------
 14 files changed, 83 insertions(+), 50 deletions(-)

diff --git a/hmp.c b/hmp.c
index f81f332..35cd625 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1698,13 +1698,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 0c675ff..65764bc 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, void *opaque,
         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 cb8237f..fc3bced 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, struct Visitor *v,
             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 3b67dc7..d1f4f78 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, void **obj, size_t size,
                          const char *name, 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 b8146d7..f83707a 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -68,12 +68,19 @@ typedef struct GenericList
 void visit_start_struct(Visitor *v, void **obj, size_t size,
                         const char *name, 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 a767391..fabf3d7 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -157,13 +157,13 @@ opts_start_struct(Visitor *v, void **obj, size_t size,


 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 92345aa..d3f9171 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, void **obj, size_t unused,
     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 09cc5c9..a3b59f7 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -36,9 +36,16 @@ void visit_start_struct(Visitor *v, void **obj, size_t size,
     v->start_struct(v, obj, size, name, 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 0587b8e..18c963b 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, void **obj, size_t size,
     }
 }

-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, QType *type, bool promote_int,
                                     const char *name, Error **errp)
@@ -342,11 +345,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 28f9854..1ffe820 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -117,7 +117,7 @@ static void qmp_output_start_struct(Visitor *v, void **obj, size_t unused,
     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);
diff --git a/qom/object.c b/qom/object.c
index 5eb65ea..36de3a2 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1904,10 +1904,9 @@ static void property_get_tm(Object *obj, Visitor *v, void *opaque,
     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 6a29b6c..83f1257 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 5c8f1e6..694951f 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -255,8 +255,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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/vl.c b/vl.c
index 1010bdb..ed1203e 100644
--- a/vl.c
+++ b/vl.c
@@ -2848,11 +2848,10 @@ static int object_create(void *opaque, QemuOpts *opts, Error **errp)
     qdict_del(pdict, "qom-type");
     visit_type_str(v, &type, "qom-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");
@@ -2862,14 +2861,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.4.3

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

* [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list()
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (27 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 28/31] qapi: Split visit_end_struct() into pieces Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  4:51   ` David Gibson
  2015-12-10 17:32   ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 30/31] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 31/31] RFC: qapi: Adjust layout of FooList types Eric Blake
  30 siblings, 2 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, Alexander Graf, open list:sPAPR pseries, armbru,
	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 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>

---
v7: new patch
---
 hw/ppc/spapr_drc.c           |  2 +-
 include/qapi/visitor-impl.h  |  5 ++--
 include/qapi/visitor.h       | 44 +++++++++++++++++--------------
 qapi/opts-visitor.c          | 32 ++++++++++-------------
 qapi/qapi-dealloc-visitor.c  | 29 +++++----------------
 qapi/qapi-visit-core.c       |  7 ++---
 qapi/qmp-input-visitor.c     | 62 +++++++++++++++++++++-----------------------
 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, 123 insertions(+), 170 deletions(-)

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 65764bc..f5ea3e0 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, void *opaque,
             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 d1f4f78..9654be6 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 f83707a..3b9f429 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -111,32 +111,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 fabf3d7..ae39531 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 d3f9171..fc66dad 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 a3b59f7..a7e6ca8 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 18c963b..20db105 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -44,16 +44,18 @@ 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);
+            qobj = qdict_get(qobject_to_qdict(qobj), name);
+        } else if (so->entry) {
+            qobj = qlist_entry_obj(so->entry);
+            so->entry = qlist_next(so->entry);
         }
     }

@@ -66,7 +68,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 +79,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 +141,7 @@ static void qmp_input_start_struct(Visitor *v, void **obj, size_t size,
         return;
     }

-    qmp_input_push(qiv, qobj, &err);
+    qmp_input_push(qiv, qobj, NULL, &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -158,10 +161,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 +174,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;
 }


@@ -361,7 +357,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 1ffe820..aa07f27 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);
 }

@@ -123,7 +119,8 @@ static void qmp_output_end_struct(Visitor *v)
     qmp_output_pop(qov);
 }

-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();
@@ -132,20 +129,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 0b0bb6e..0adbe79 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, int64_t *obj, const char *name,
@@ -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 13b00c7..1410d9b 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, double *obj, const char *name,
 }

 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 694951f..6d4f6ba 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, %(c_name)s **obj, const char *name, 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, &native_i->value, NULL, &err);
+    elt = *obj;
+    while (elt) {
+        visit_type_%(c_elt_type)s(v, &elt->value, NULL, &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.4.3

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

* [Qemu-devel] [PATCH v7 30/31] qapi: Change visit_type_FOO() to no longer return partial objects
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (28 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list() Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 31/31] RFC: qapi: Adjust layout of FooList types Eric Blake
  30 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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>

---
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         | 43 +++++++++++++++++++++++++++-------
 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, 156 insertions(+), 81 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 9654be6..ccb8b19 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, void **obj, size_t size,
+    bool (*start_struct)(Visitor *v, void **obj, size_t size,
                          const char *name, 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 3b9f429..e9bb811 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -30,6 +30,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
@@ -61,11 +82,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, void **obj, size_t size,
+bool visit_start_struct(Visitor *v, void **obj, size_t size,
                         const char *name, Error **errp);
 /**
  * Prepare for completing a struct visit.
@@ -84,19 +106,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.
@@ -125,11 +143,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 ae39531..6dd22e2 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, void **obj, size_t size,
                   const char *name, 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, void **obj, size_t size,
         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 fc66dad..42f366a 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, void **obj, size_t unused,
+static bool qapi_dealloc_start_struct(Visitor *v, void **obj, size_t unused,
                                       const char *name, 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 a7e6ca8..c703002 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -26,14 +26,20 @@ static bool visit_is_output(Visitor *v)
     return v->type_enum == output_type_enum;
 }

-void visit_start_struct(Visitor *v, void **obj, size_t size,
+bool visit_start_struct(Visitor *v, void **obj, size_t size,
                         const char *name, Error **errp)
 {
+    bool result;
+
     assert(obj ? size : !size);
     if (obj && visit_is_output(v)) {
         assert(*obj);
     }
-    v->start_struct(v, obj, size, name, errp);
+    result = v->start_struct(v, obj, size, name, 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)
@@ -224,6 +240,7 @@ 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)
 {
+    Error *err = NULL;
     assert(obj);
     /* TODO: Fix callers to not pass NULL when they mean "", so that we
      * can enable:
@@ -231,7 +248,11 @@ void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
         assert(*obj);
     }
      */
-    v->type_str(v, obj, name, errp);
+    v->type_str(v, obj, name, &err);
+    if (!visit_is_output(v) && err) {
+        *obj = NULL;
+    }
+    error_propagate(errp, err);
 }

 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
@@ -243,11 +264,17 @@ 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)
 {
+    Error *err = NULL;
+
     assert(obj);
     if (visit_is_output(v)) {
         assert(*obj);
     }
-    v->type_any(v, obj, name, errp);
+    v->type_any(v, obj, name, &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 20db105..263ac97 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -128,7 +128,7 @@ static void qmp_input_pop(Visitor *v)
     qiv->nb_stack--;
 }

-static void qmp_input_start_struct(Visitor *v, void **obj, size_t size,
+static bool qmp_input_start_struct(Visitor *v, void **obj, size_t size,
                                    const char *name, Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -138,30 +138,34 @@ static void qmp_input_start_struct(Visitor *v, void **obj, size_t size,
     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);
@@ -171,7 +175,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));
@@ -179,10 +183,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 aa07f27..dd08a61 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -103,7 +103,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
     }
 }

-static void qmp_output_start_struct(Visitor *v, void **obj, size_t unused,
+static bool qmp_output_start_struct(Visitor *v, void **obj, size_t unused,
                                     const char *name, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
@@ -111,6 +111,7 @@ static void qmp_output_start_struct(Visitor *v, void **obj, size_t unused,

     qmp_output_add(qov, name, dict);
     qmp_output_push(qov, dict);
+    return false;
 }

 static void qmp_output_end_struct(Visitor *v)
@@ -119,7 +120,7 @@ static void qmp_output_end_struct(Visitor *v)
     qmp_output_pop(qov);
 }

-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);
@@ -127,6 +128,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 0adbe79..4b8f1a5 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 1410d9b..29b8f50 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -263,7 +263,7 @@ static void print_type_number(Visitor *v, double *obj, const char *name,
     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 6d4f6ba..9e6e7a6 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, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, Error **errp)
 {
     Error *err = NULL;
+    bool allocated;

-    visit_start_struct(v, (void **)obj, sizeof(%(c_name)s), name, &err);
+    allocated = visit_start_struct(v, (void **)obj, sizeof(%(c_name)s),
+                                   name, &err);
     if (err) {
         goto out;
     }
@@ -301,10 +316,15 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, 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 b132775..f03cdf5 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 f1c2e3b..9792277 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, &p, NULL, &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, &udp, NULL, &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, &head, NULL, &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, &tmp, NULL, &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, &tmp, NULL, &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, &tmp, NULL, &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, &tmp, NULL, &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 b4a5bee..45d06f3 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -708,18 +708,12 @@ static void test_visitor_in_errors(TestInputVisitorData *data,

     visit_type_TestStruct(v, &p, NULL, &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, &q, NULL, &err);
     error_free_or_abort(&err);
-    assert(q);
-    qapi_free_strList(q);
+    assert(!q);
 }

 static void test_visitor_in_wrong_type(TestInputVisitorData *data,
-- 
2.4.3

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

* [Qemu-devel] [PATCH v7 31/31] RFC: qapi: Adjust layout of FooList types
  2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (29 preceding siblings ...)
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 30/31] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
@ 2015-12-08  3:55 ` Eric Blake
  2015-12-08  4:54   ` David Gibson
  30 siblings, 1 reply; 41+ messages in thread
From: Eric Blake @ 2015-12-08  3:55 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, Alexander Graf, open list:sPAPR pseries, armbru,
	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>

---
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 f5ea3e0..6d07393 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, void *opaque,
             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 ccb8b19..71313a9 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 e9bb811..febe5da 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -55,11 +55,8 @@
  * created by the qapi generator. */
 typedef struct GenericList
 {
-    union {
-        void *value;
-        uint64_t padding;
-    };
     struct GenericList *next;
+    char padding[];
 } GenericList;

 /**
@@ -129,19 +126,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
@@ -149,17 +146,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 6dd22e2..573249a 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 42f366a..add38b5 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 c703002..07865e4 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 263ac97..c56ff3d 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -166,7 +166,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);
@@ -182,7 +182,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;
@@ -192,7 +192,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];
@@ -200,7 +200,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 dd08a61..2a7b239 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -121,7 +121,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();
@@ -132,7 +133,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 4b8f1a5..8a9e385 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 29b8f50..0611c6d 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -264,7 +264,8 @@ static void print_type_number(Visitor *v, double *obj, const char *name,
 }

 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 91c5ae0..7c1afba 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 9e6e7a6..108823d 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -130,7 +130,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, 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, %(c_name)s **obj, const char *name, 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.4.3

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

* Re: [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
@ 2015-12-08  4:40   ` David Gibson
  2015-12-11 13:51   ` Eric Blake
  1 sibling, 0 replies; 41+ messages in thread
From: David Gibson @ 2015-12-08  4:40 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael S. Tsirkin, qemu-devel, armbru, Michael Roth,
	Alexander Graf, open list:sPAPR (pseries),
	Paolo Bonzini, Luiz Capitulino, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 1425 bytes --]

On Mon, Dec 07, 2015 at 08:55:03PM -0700, Eric Blake wrote:
> 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.  While at it, change
> the signature of visit_start_struct() to place the 'name'
> argument at the end (other than 'errp'), and the 'size' argument
> next to 'obj'; this placement of 'name' matches matches how all
> other functions in visit.h do it (visit_type_enum() places
> 'strings' between 'obj' and 'name'; visit_get_next_type() places
> 'promote_int' between 'type' and 'name').  This also avoids the
> confusion caused by splitting related pieces of information,
> where the old signature an unrelated parameter in between the
> "typename" and sizeof(typename) arguments.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>

For spapr parts:

Acked-by: David Gibson <david@gibson.dropbear.id.au>

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [Qemu-devel] [PATCH v7 14/31] qapi: Drop unused error argument for list and implicit struct
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 14/31] qapi: Drop unused error argument for list and implicit struct Eric Blake
@ 2015-12-08  4:40   ` David Gibson
  0 siblings, 0 replies; 41+ messages in thread
From: David Gibson @ 2015-12-08  4:40 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Alexander Graf, open list:sPAPR (pseries),
	qemu-devel, armbru

[-- Attachment #1: Type: text/plain, Size: 900 bytes --]

On Mon, Dec 07, 2015 at 08:55:04PM -0700, Eric Blake wrote:
> 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.
> 
> A later patch will then tackle the larger task of splitting
> visit_end_struct(), which can indeed set an error.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>

For spapr parts:

Acked-by: David Gibson <david@gibson.dropbear.id.au>

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [Qemu-devel] [PATCH v7 20/31] spapr_drc: Expose 'null' in qom-get when there is no fdt
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 20/31] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
@ 2015-12-08  4:40   ` David Gibson
  0 siblings, 0 replies; 41+ messages in thread
From: David Gibson @ 2015-12-08  4:40 UTC (permalink / raw)
  To: Eric Blake; +Cc: Alexander Graf, open list:sPAPR (pseries), qemu-devel, armbru

[-- Attachment #1: Type: text/plain, Size: 1513 bytes --]

On Mon, Dec 07, 2015 at 08:55:10PM -0700, Eric Blake wrote:
> 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 1d10b44; but that this time,
> the change is intentional and not an accidental side-effect.)
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Cc: David Gibson <david@gibson.dropbear.id.au>

Acked-by: David Gibson <david@gibson.dropbear.id.au>

> 
> ---
> 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 dcce563..0c675ff 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, void *opaque,
>      void *fdt;
> 
>      if (!drc->fdt) {
> -        visit_start_struct(v, NULL, 0, name, &err);
> -        if (!err) {
> -            visit_end_struct(v, &err);
> -        }
> -        error_propagate(errp, err);
> +        visit_type_null(v, NULL, errp);
>          return;
>      }
> 

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [Qemu-devel] [PATCH v7 28/31] qapi: Split visit_end_struct() into pieces
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 28/31] qapi: Split visit_end_struct() into pieces Eric Blake
@ 2015-12-08  4:42   ` David Gibson
  0 siblings, 0 replies; 41+ messages in thread
From: David Gibson @ 2015-12-08  4:42 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael S. Tsirkin, qemu-devel, armbru, Michael Roth,
	Alexander Graf, open list:sPAPR (pseries),
	Paolo Bonzini, Luiz Capitulino, Andreas Färber

[-- Attachment #1: Type: text/plain, Size: 1470 bytes --]

On Mon, Dec 07, 2015 at 08:55:18PM -0700, Eric Blake wrote:
> 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>

For spapr parts:

Acked-by: David Gibson <david@gibson.dropbear.id.au>

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list()
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list() Eric Blake
@ 2015-12-08  4:51   ` David Gibson
  2015-12-10 17:32   ` Eric Blake
  1 sibling, 0 replies; 41+ messages in thread
From: David Gibson @ 2015-12-08  4:51 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Alexander Graf, open list:sPAPR (pseries),
	qemu-devel, armbru

[-- Attachment #1: Type: text/plain, Size: 2945 bytes --]

On Mon, Dec 07, 2015 at 08:55:19PM -0700, Eric Blake wrote:
> 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 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>

For the spapr change:

Acked-by: David Gibson <david@gibson.dropbear.id.au>
-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [Qemu-devel] [PATCH v7 31/31] RFC: qapi: Adjust layout of FooList types
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 31/31] RFC: qapi: Adjust layout of FooList types Eric Blake
@ 2015-12-08  4:54   ` David Gibson
  0 siblings, 0 replies; 41+ messages in thread
From: David Gibson @ 2015-12-08  4:54 UTC (permalink / raw)
  To: Eric Blake
  Cc: Michael Roth, Alexander Graf, open list:sPAPR (pseries),
	qemu-devel, armbru

[-- Attachment #1: Type: text/plain, Size: 1689 bytes --]

On Mon, Dec 07, 2015 at 08:55:21PM -0700, Eric Blake wrote:
> 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>

For the spapr change

Acked-by: David Gibson <david@gibson.dropbear.id.au>

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list()
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list() Eric Blake
  2015-12-08  4:51   ` David Gibson
@ 2015-12-10 17:32   ` Eric Blake
  2015-12-11  4:04     ` Eric Blake
  1 sibling, 1 reply; 41+ messages in thread
From: Eric Blake @ 2015-12-10 17:32 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, David Gibson, open list:sPAPR pseries, Michael Roth,
	Alexander Graf

[-- Attachment #1: Type: text/plain, Size: 1070 bytes --]

On 12/07/2015 08:55 PM, Eric Blake wrote:

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

> +++ 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);

Looking at this, it is inconsistent with start_struct (where name comes
AFTER the object); that is, I think this should probably be:

void (*start_list)(Visitor *v, GenericList **list, const char *name,
Error **errp);

although I'll still wait for review comments before respinning.

-- 
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] 41+ messages in thread

* Re: [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list()
  2015-12-10 17:32   ` Eric Blake
@ 2015-12-11  4:04     ` Eric Blake
  0 siblings, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-11  4:04 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael Roth, Alexander Graf, open list:sPAPR pseries, armbru,
	David Gibson

[-- Attachment #1: Type: text/plain, Size: 932 bytes --]

On 12/10/2015 10:32 AM, Eric Blake wrote:
> On 12/07/2015 08:55 PM, Eric Blake wrote:
> 
>> 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)
>> }
>>

> Looking at this, it is inconsistent with start_struct (where name comes
> AFTER the object); that is, I think this should probably be:
> 
> void (*start_list)(Visitor *v, GenericList **list, const char *name,
> Error **errp);
> 
> although I'll still wait for review comments before respinning.

I've also bisected this patch as causing assertion failures in
qemu-iotests 041 and 139, so I'll have to sort that out on the 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] 41+ messages in thread

* Re: [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit
  2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
  2015-12-08  4:40   ` David Gibson
@ 2015-12-11 13:51   ` Eric Blake
  1 sibling, 0 replies; 41+ messages in thread
From: Eric Blake @ 2015-12-11 13:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael S. Tsirkin, armbru, Michael Roth, Alexander Graf,
	open list:sPAPR pseries, Paolo Bonzini, Luiz Capitulino,
	Andreas Färber, David Gibson

[-- Attachment #1: Type: text/plain, Size: 1867 bytes --]

On 12/07/2015 08:55 PM, Eric Blake wrote:
> 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.  While at it, change
> the signature of visit_start_struct() to place the 'name'
> argument at the end (other than 'errp'), and the 'size' argument
> next to 'obj'; this placement of 'name' matches matches how all
> other functions in visit.h do it (visit_type_enum() places
> 'strings' between 'obj' and 'name'; visit_get_next_type() places
> 'promote_int' between 'type' and 'name').  This also avoids the
> confusion caused by splitting related pieces of information,
> where the old signature an unrelated parameter in between the
> "typename" and sizeof(typename) arguments.

I should probably spell it out better in the commit message; I was going
from:

visit_start_struct(v, obj, [kind,] name, size, err)

to:

visit_start_struct(v, obj, size, [kind,] name, err)

then dropping kind as unused.  But I'm seriously thinking about doing
the argument shuffle in the opposite direction (move name earlier,
rather than later):

visit_start_struct(v, name, obj, [kind,] size, err)

with a global coccinelle patch that changes ALL visit_type_* to put name
before obj, because after all we are tying this to JSON which uses
"name":value and it looks odd that every one of our visitor functions
takes parameters in 'value, name' 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] 41+ messages in thread

end of thread, other threads:[~2015-12-11 13:52 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-08  3:54 [Qemu-devel] [PATCH v7 00/31] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 01/31] qobject: Document more shortcomings in our number handling Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 02/31] qapi: Avoid use of misnamed DO_UPCAST() Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 03/31] qapi: Drop dead dealloc visitor variable Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 04/31] hmp: Improve use of qapi visitor Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 05/31] vl: " Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 06/31] balloon: " Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 07/31] qapi: Improve generated event " Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 08/31] qapi: Track all failures between visit_start/stop Eric Blake
2015-12-08  3:54 ` [Qemu-devel] [PATCH v7 09/31] qapi: Prefer type_int64 over type_int in visitors Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 10/31] qapi: Make all visitors supply uint64 callbacks Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 11/31] qapi: Consolidate visitor small integer callbacks Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 12/31] qapi: Don't cast Enum* to int* Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 13/31] qapi: Drop unused 'kind' for struct/enum visit Eric Blake
2015-12-08  4:40   ` David Gibson
2015-12-11 13:51   ` Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 14/31] qapi: Drop unused error argument for list and implicit struct Eric Blake
2015-12-08  4:40   ` David Gibson
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 15/31] qmp: Fix reference-counting of qnull on empty output visit Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 16/31] qmp: Don't abuse stack to track qmp-output root Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 17/31] qapi: Document visitor interfaces, add assertions Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 18/31] qapi: Add visit_type_null() visitor Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 19/31] qmp: Tighten output visitor rules Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 20/31] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
2015-12-08  4:40   ` David Gibson
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 21/31] qapi: Simplify excess input reporting in input visitors Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 22/31] qapi: Add type.is_empty() helper Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 23/31] qapi: Fix command with named empty argument type Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 24/31] qapi: Eliminate empty visit_type_FOO_fields Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 25/31] qapi: Canonicalize missing object to :empty Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 26/31] qapi-visit: Unify struct and union visit Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 27/31] qapi: Rework deallocation of partial struct Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 28/31] qapi: Split visit_end_struct() into pieces Eric Blake
2015-12-08  4:42   ` David Gibson
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 29/31] qapi: Simplify semantics of visit_next_list() Eric Blake
2015-12-08  4:51   ` David Gibson
2015-12-10 17:32   ` Eric Blake
2015-12-11  4:04     ` Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 30/31] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
2015-12-08  3:55 ` [Qemu-devel] [PATCH v7 31/31] RFC: qapi: Adjust layout of FooList types Eric Blake
2015-12-08  4:54   ` David Gibson

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.