All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
@ 2016-04-28 21:45 Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 01/24] qapi-visit: Add visitor.type classification Eric Blake
                   ` (24 more replies)
  0 siblings, 25 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

2.7 material; much less churn this time through, but enough
that it was easier for me to repost than to make Markus do
the touchups his review found.

Based on master, with no prerequisite patches.

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

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

v15 was:
https://lists.gnu.org/archive/html/qemu-devel/2016-04/msg04147.html

Since then, I deferred the qmp-commands.hx patch to later
(Marc-André's solution may be better), and split two others
while addressing review comments.

001/24:[----] [--] 'qapi-visit: Add visitor.type classification'
002/24:[0014] [FC] 'qapi: Guarantee NULL obj on input visitor callback error'
003/24:[----] [--] 'qmp: Drop dead command->type'
004/24:[----] [--] 'qmp-input: Clean up stack handling'
005/24:[down] 'qapi: Consolidate QMP input visitor creation'
006/24:[0042] [FC] 'qapi: Use strict QMP input visitor in more places'
007/24:[----] [--] 'qmp-input: Don't consume input when checking has_member'
008/24:[----] [--] 'qapi-commands: Wrap argument visit in visit_start_struct'
009/24:[0025] [FC] 'qom: Wrap prop visit in visit_start_struct'
010/24:[0002] [FC] 'qmp-input: Require struct push to visit members of top dict'
011/24:[0004] [FC] 'qmp-input: Refactor when list is advanced'
012/24:[0021] [FC] 'qapi: Document visitor interfaces, add assertions'
013/24:[----] [--] 'tests: Add check-qnull'
014/24:[0005] [FC] 'qapi: Add visit_type_null() visitor'
015/24:[0014] [FC] 'qmp: Support explicit null during visits'
016/24:[----] [--] 'spapr_drc: Expose 'null' in qom-get when there is no fdt'
017/24:[----] [--] 'qmp: Add qmp_output_visitor_reset()'
018/24:[----] [--] 'qmp: Tighten output visitor rules'
019/24:[0010] [FC] 'qapi: Split visit_end_struct() into pieces'
020/24:[down] 'qapi: Don't pass NULL to printf in string input visitor'
021/24:[----] [--] 'tests/string-input-visitor: Add negative integer tests'
022/24:[0002] [FC] 'qapi: Fix string input visitor handling of invalid list'
023/24:[0010] [FC] 'qapi: Simplify semantics of visit_next_list()'
024/24:[0012] [FC] 'qapi: Change visit_type_FOO() to no longer return partial objects'

Eric Blake (23):
  qapi-visit: Add visitor.type classification
  qapi: Guarantee NULL obj on input visitor callback error
  qmp: Drop dead command->type
  qmp-input: Clean up stack handling
  qapi: Consolidate QMP input visitor creation
  qapi: Use strict QMP input visitor in more places
  qmp-input: Don't consume input when checking has_member
  qapi-commands: Wrap argument visit in visit_start_struct
  qom: Wrap prop visit in visit_start_struct
  qmp-input: Require struct push to visit members of top dict
  qmp-input: Refactor when list is advanced
  qapi: Document visitor interfaces, add assertions
  tests: Add check-qnull
  qapi: Add visit_type_null() visitor
  qmp: Support explicit null during visits
  spapr_drc: Expose 'null' in qom-get when there is no fdt
  qmp: Add qmp_output_visitor_reset()
  qmp: Tighten output visitor rules
  qapi: Split visit_end_struct() into pieces
  qapi: Don't pass NULL to printf in string input visitor
  qapi: Fix string input visitor handling of invalid list
  qapi: Simplify semantics of visit_next_list()
  qapi: Change visit_type_FOO() to no longer return partial objects

Markus Armbruster (1):
  tests/string-input-visitor: Add negative integer tests

 include/qapi/visitor.h               | 492 +++++++++++++++++++++++++++++++++--
 include/qapi/visitor-impl.h          |  81 ++++--
 scripts/qapi-commands.py             |  12 +-
 scripts/qapi-event.py                |   5 +-
 scripts/qapi-visit.py                |  53 ++--
 include/qapi/dealloc-visitor.h       |   5 +
 include/qapi/opts-visitor.h          |   5 +
 include/qapi/qmp-input-visitor.h     |   9 +-
 include/qapi/qmp-output-visitor.h    |   1 +
 include/qapi/qmp/dispatch.h          |   6 -
 include/qapi/string-input-visitor.h  |   5 +
 include/qapi/string-output-visitor.h |   5 +
 qapi/qapi-visit-core.c               | 111 ++++++--
 block/crypto.c                       |  14 +-
 hw/ppc/spapr_drc.c                   |  11 +-
 hw/virtio/virtio-balloon.c           |  15 +-
 qapi/opts-visitor.c                  |  70 ++---
 qapi/qapi-dealloc-visitor.c          |  43 +--
 qapi/qmp-dispatch.c                  |  18 +-
 qapi/qmp-input-visitor.c             | 189 ++++++++------
 qapi/qmp-output-visitor.c            |  71 ++---
 qapi/qmp-registry.c                  |   1 -
 qapi/string-input-visitor.c          |  51 ++--
 qapi/string-output-visitor.c         |  43 ++-
 qmp.c                                |   2 +-
 qom/object.c                         |   5 +-
 qom/object_interfaces.c              |  42 +--
 qom/qom-qobject.c                    |   3 +-
 replay/replay-input.c                |   2 +-
 tests/check-qnull.c                  |  75 ++++++
 tests/test-qmp-commands.c            |  15 +-
 tests/test-qmp-input-strict.c        |  21 +-
 tests/test-qmp-input-visitor.c       |  42 ++-
 tests/test-qmp-output-visitor.c      |  29 ++-
 tests/test-string-input-visitor.c    |  23 +-
 tests/test-visitor-serialization.c   |   2 +-
 util/qemu-sockets.c                  |   2 +-
 docs/qapi-code-gen.txt               |  44 +++-
 tests/.gitignore                     |   1 +
 tests/Makefile                       |   6 +-
 40 files changed, 1198 insertions(+), 432 deletions(-)
 create mode 100644 tests/check-qnull.c

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 01/24] qapi-visit: Add visitor.type classification
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error Eric Blake
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We have three classes of QAPI visitors: input, output, and dealloc.
Currently, all implementations of these visitors have one thing in
common based on their visitor type: the implementation used for the
visit_type_enum() callback.  But since we plan to add more such
common behavior, in relation to documenting and further refining
the semantics, it makes more sense to have the visitor
implementations advertise which class they belong to, so the common
qapi-visit-core code can use that information in multiple places.

A later patch will better document the types of visitors directly
in visitor.h.

For this patch, knowing the class of a visitor implementation lets
us make input_type_enum() and output_type_enum() become static
functions, by replacing the callback function Visitor.type_enum()
with the simpler enum member Visitor.type.  Share a common
assertion in qapi-visit-core as part of the refactoring.

Move comments in opts-visitor.c to match the refactored layout.

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

---
v15: comment improvements
v14: no change
v13: no change
v12: new patch
---
 include/qapi/visitor.h       | 11 +++++++++++
 include/qapi/visitor-impl.h  | 23 ++++++++++++++---------
 qapi/qapi-visit-core.c       | 28 +++++++++++++++-------------
 qapi/opts-visitor.c          | 17 +++++++----------
 qapi/qapi-dealloc-visitor.c  |  7 +------
 qapi/qmp-input-visitor.c     |  2 +-
 qapi/qmp-output-visitor.c    |  2 +-
 qapi/string-input-visitor.c  |  2 +-
 qapi/string-output-visitor.c |  2 +-
 9 files changed, 52 insertions(+), 42 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 9a8d010..690589f 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -78,8 +78,19 @@ void visit_end_alternate(Visitor *v);
  */
 bool visit_optional(Visitor *v, const char *name, bool *present);

+/*
+ * Visit an enum value.
+ *
+ * @strings expresses the mapping between C enum values and QAPI enum
+ * names; it should be the ENUM_lookup array from visit-types.h.
+ *
+ * May call visit_type_str() under the hood, and the enum visit may
+ * fail even if the corresponding string visit succeeded; this implies
+ * that visit_type_str() must have no unwelcome side effects.
+ */
 void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp);
+
 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
 void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
                       Error **errp);
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 2bd8f29..51c338a 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -14,6 +14,17 @@

 #include "qapi/visitor.h"

+/*
+ * There are three classes of visitors; setting the class determines
+ * how QAPI enums are visited, as well as what additional restrictions
+ * can be asserted.
+ */
+typedef enum VisitorType {
+    VISITOR_INPUT,
+    VISITOR_OUTPUT,
+    VISITOR_DEALLOC,
+} VisitorType;
+
 struct Visitor
 {
     /* Must be set */
@@ -36,10 +47,6 @@ struct Visitor
     void (*end_alternate)(Visitor *v);

     /* Must be set. */
-    void (*type_enum)(Visitor *v, const char *name, int *obj,
-                      const char *const strings[], Error **errp);
-
-    /* Must be set. */
     void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
                        Error **errp);
     /* Must be set. */
@@ -58,11 +65,9 @@ struct Visitor

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

-void input_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], Error **errp);
-void output_type_enum(Visitor *v, const char *name, int *obj,
-                      const char *const strings[], Error **errp);
-
 #endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index fa680c9..3cd7edc 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -72,12 +72,6 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
     return *present;
 }

-void visit_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], Error **errp)
-{
-    v->type_enum(v, name, obj, strings, errp);
-}
-
 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
 {
     v->type_int64(v, name, obj, errp);
@@ -208,14 +202,13 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
     v->type_any(v, name, obj, errp);
 }

-void output_type_enum(Visitor *v, const char *name, int *obj,
-                      const char *const strings[], Error **errp)
+static void output_type_enum(Visitor *v, const char *name, int *obj,
+                             const char *const strings[], Error **errp)
 {
     int i = 0;
     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");
@@ -226,15 +219,13 @@ void output_type_enum(Visitor *v, const char *name, int *obj,
     visit_type_str(v, name, &enum_str, errp);
 }

-void input_type_enum(Visitor *v, const char *name, int *obj,
-                     const char *const strings[], Error **errp)
+static void input_type_enum(Visitor *v, const char *name, int *obj,
+                            const char *const strings[], Error **errp)
 {
     Error *local_err = NULL;
     int64_t value = 0;
     char *enum_str;

-    assert(strings);
-
     visit_type_str(v, name, &enum_str, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
@@ -257,3 +248,14 @@ void input_type_enum(Visitor *v, const char *name, int *obj,
     g_free(enum_str);
     *obj = value;
 }
+
+void visit_type_enum(Visitor *v, const char *name, int *obj,
+                     const char *const strings[], Error **errp)
+{
+    assert(strings);
+    if (v->type == VISITOR_INPUT) {
+        input_type_enum(v, name, obj, strings, errp);
+    } else if (v->type == VISITOR_OUTPUT) {
+        output_type_enum(v, name, obj, strings, errp);
+    }
+}
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 602f260..66aeaed 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -317,6 +317,11 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
         return;
     }
     *obj = g_strdup(opt->str ? opt->str : "");
+    /* Note that we consume a string even if this is called as part of
+     * an enum visit that later fails because the string is not a
+     * valid enum value; this is harmless because tracking what gets
+     * consumed only matters to visit_end_struct() as the final error
+     * check if there were no other failures during the visit.  */
     processed(ov, name);
 }

@@ -507,6 +512,8 @@ opts_visitor_new(const QemuOpts *opts)

     ov = g_malloc0(sizeof *ov);

+    ov->visitor.type = VISITOR_INPUT;
+
     ov->visitor.start_struct = &opts_start_struct;
     ov->visitor.end_struct   = &opts_end_struct;

@@ -514,16 +521,6 @@ opts_visitor_new(const QemuOpts *opts)
     ov->visitor.next_list  = &opts_next_list;
     ov->visitor.end_list   = &opts_end_list;

-    /* input_type_enum() covers both "normal" enums and union discriminators.
-     * The union discriminator field is always generated as "type"; it should
-     * match the "type" QemuOpt child of any QemuOpts.
-     *
-     * input_type_enum() will remove the looked-up key from the
-     * "unprocessed_opts" hash even if the lookup fails, because the removal is
-     * done earlier in opts_type_str(). This should be harmless.
-     */
-    ov->visitor.type_enum = &input_type_enum;
-
     ov->visitor.type_int64  = &opts_type_int64;
     ov->visitor.type_uint64 = &opts_type_uint64;
     ov->visitor.type_size   = &opts_type_size;
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 6922179..c19a459 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -163,11 +163,6 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name,
     }
 }

-static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
-                                   const char * const strings[], Error **errp)
-{
-}
-
 Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
 {
     return &v->visitor;
@@ -184,6 +179,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)

     v = g_malloc0(sizeof(*v));

+    v->visitor.type = VISITOR_DEALLOC;
     v->visitor.start_struct = qapi_dealloc_start_struct;
     v->visitor.end_struct = qapi_dealloc_end_struct;
     v->visitor.start_alternate = qapi_dealloc_start_alternate;
@@ -191,7 +187,6 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.start_list = qapi_dealloc_start_list;
     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_int64 = qapi_dealloc_type_int64;
     v->visitor.type_uint64 = qapi_dealloc_type_uint64;
     v->visitor.type_bool = qapi_dealloc_type_bool;
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 7cd1b77..02d4233 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -339,13 +339,13 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)

     v = g_malloc0(sizeof(*v));

+    v->visitor.type = VISITOR_INPUT;
     v->visitor.start_struct = qmp_input_start_struct;
     v->visitor.end_struct = qmp_input_end_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.start_alternate = qmp_input_start_alternate;
-    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;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index d44c676..1f2a7ba 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -234,12 +234,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void)

     v = g_malloc0(sizeof(*v));

+    v->visitor.type = VISITOR_OUTPUT;
     v->visitor.start_struct = qmp_output_start_struct;
     v->visitor.end_struct = qmp_output_end_struct;
     v->visitor.start_list = qmp_output_start_list;
     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_int64 = qmp_output_type_int64;
     v->visitor.type_uint64 = qmp_output_type_uint64;
     v->visitor.type_bool = qmp_output_type_bool;
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index ab12953..d604575 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -348,7 +348,7 @@ StringInputVisitor *string_input_visitor_new(const char *str)

     v = g_malloc0(sizeof(*v));

-    v->visitor.type_enum = input_type_enum;
+    v->visitor.type = VISITOR_INPUT;
     v->visitor.type_int64 = parse_type_int64;
     v->visitor.type_uint64 = parse_type_uint64;
     v->visitor.type_size = parse_type_size;
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index c2e5c5b..0d44d7e 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -351,7 +351,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 = VISITOR_OUTPUT;
     v->visitor.type_int64 = print_type_int64;
     v->visitor.type_uint64 = print_type_uint64;
     v->visitor.type_size = print_type_size;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 01/24] qapi-visit: Add visitor.type classification Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-29  8:28   ` Markus Armbruster
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 03/24] qmp: Drop dead command->type Eric Blake
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Our existing input visitors were not very consistent on errors
in a function taking 'TYPE **obj' (that is, start_struct(),
start_alternate(), type_str(), and type_any(). next_list() is
similar, except that since commit 08f9541, it can't fail).
While all of them set '*obj' to allocated storage on success,
it was not obvious whether '*obj' was guaranteed safe on failure,
or whether it was left uninitialized.  But a future patch wants
to guarantee that visit_type_FOO() does not leak a partially-
constructed obj back to the caller; it is easier to implement
this if we can reliably state that input visitors assign '*obj'
regardless of success or failure, and that on failure *obj is
NULL.  Add assertions to enforce consistency in the final
setting of err vs. *obj.

The opts-visitor start_struct() doesn't set an error, but it
also was doing a weird check for 0 size; all callers pass in
non-zero size if obj is non-NULL.

The testsuite has at least one spot where we no longer need
to pre-initialize a variable prior to a visit; valgrind confirms
that the test is still fine with the cleanup.

A later patch will document the design constraint implemented
here.

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

---
v16: tighten assertions, another commit message tweak
v15: enhance commit message, hoist assertions from later in series
v14: no change
v13: no change
v12: new patch
---
 qapi/qapi-visit-core.c        | 34 ++++++++++++++++++++++++++++++----
 qapi/opts-visitor.c           |  3 ++-
 qapi/qmp-input-visitor.c      |  4 ++++
 qapi/string-input-visitor.c   |  1 +
 tests/test-qmp-input-strict.c |  2 +-
 5 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 3cd7edc..7ad5ff4 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -23,7 +23,13 @@
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp)
 {
-    v->start_struct(v, name, obj, size, errp);
+    Error *err = NULL;
+
+    v->start_struct(v, name, obj, size, &err);
+    if (obj && v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 void visit_end_struct(Visitor *v, Error **errp)
@@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
                            GenericAlternate **obj, size_t size,
                            bool promote_int, Error **errp)
 {
+    Error *err = NULL;
+
     assert(obj && size >= sizeof(GenericAlternate));
     if (v->start_alternate) {
-        v->start_alternate(v, name, obj, size, promote_int, errp);
+        v->start_alternate(v, name, obj, size, promote_int, &err);
     }
+    if (v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 void visit_end_alternate(Visitor *v)
@@ -188,7 +200,14 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)

 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
-    v->type_str(v, name, obj, errp);
+    Error *err = NULL;
+
+    assert(obj);
+    v->type_str(v, name, obj, &err);
+    if (v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 void visit_type_number(Visitor *v, const char *name, double *obj,
@@ -199,7 +218,14 @@ void visit_type_number(Visitor *v, const char *name, double *obj,

 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 {
-    v->type_any(v, name, obj, errp);
+    Error *err = NULL;
+
+    assert(obj);
+    v->type_any(v, name, obj, &err);
+    if (v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 static void output_type_enum(Visitor *v, const char *name, int *obj,
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 66aeaed..4cb6436 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -133,7 +133,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
     const QemuOpt *opt;

     if (obj) {
-        *obj = g_malloc0(size > 0 ? size : 1);
+        *obj = g_malloc0(size);
     }
     if (ov->depth++ > 0) {
         return;
@@ -314,6 +314,7 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)

     opt = lookup_scalar(ov, name, errp);
     if (!opt) {
+        *obj = NULL;
         return;
     }
     *obj = g_strdup(opt->str ? opt->str : "");
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 02d4233..77cce8b 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -120,6 +120,9 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
     QObject *qobj = qmp_input_get_object(qiv, name, true);
     Error *err = NULL;

+    if (obj) {
+        *obj = NULL;
+    }
     if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "QDict");
@@ -267,6 +270,7 @@ static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
     QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));

     if (!qstr) {
+        *obj = NULL;
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "string");
         return;
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index d604575..797973a 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -293,6 +293,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
     if (siv->string) {
         *obj = g_strdup(siv->string);
     } else {
+        *obj = NULL;
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "string");
     }
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index d71727e..d5f80ec 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -263,7 +263,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
 static void test_validate_fail_alternate(TestInputVisitorData *data,
                                          const void *unused)
 {
-    UserDefAlternate *tmp = NULL;
+    UserDefAlternate *tmp;
     Visitor *v;
     Error *err = NULL;

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 03/24] qmp: Drop dead command->type
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 01/24] qapi-visit: Add visitor.type classification Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 04/24] qmp-input: Clean up stack handling Eric Blake
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Ever since QMP was first added back in commit 43c20a43, we have
never had any QmpCommandType other than QCT_NORMAL.  It's
pointless to carry around the cruft.

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

---
v15: no change
v14: no change
v13: no change
v12: new patch
---
 include/qapi/qmp/dispatch.h |  6 ------
 qapi/qmp-dispatch.c         | 18 +++++++-----------
 qapi/qmp-registry.c         |  1 -
 3 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index 4955209..5609946 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -19,11 +19,6 @@

 typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);

-typedef enum QmpCommandType
-{
-    QCT_NORMAL,
-} QmpCommandType;
-
 typedef enum QmpCommandOptions
 {
     QCO_NO_OPTIONS = 0x0,
@@ -33,7 +28,6 @@ typedef enum QmpCommandOptions
 typedef struct QmpCommand
 {
     const char *name;
-    QmpCommandType type;
     QmpCommandFunc *fn;
     QmpCommandOptions options;
     QTAILQ_ENTRY(QmpCommand) node;
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 510a1ae..08faf85 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -94,17 +94,13 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
         QINCREF(args);
     }

-    switch (cmd->type) {
-    case QCT_NORMAL:
-        cmd->fn(args, &ret, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-        } else if (cmd->options & QCO_NO_SUCCESS_RESP) {
-            g_assert(!ret);
-        } else if (!ret) {
-            ret = QOBJECT(qdict_new());
-        }
-        break;
+    cmd->fn(args, &ret, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+    } else if (cmd->options & QCO_NO_SUCCESS_RESP) {
+        g_assert(!ret);
+    } else if (!ret) {
+        ret = QOBJECT(qdict_new());
     }

     QDECREF(args);
diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c
index 4ebfbcc..4332a68 100644
--- a/qapi/qmp-registry.c
+++ b/qapi/qmp-registry.c
@@ -25,7 +25,6 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn,
     QmpCommand *cmd = g_malloc0(sizeof(*cmd));

     cmd->name = name;
-    cmd->type = QCT_NORMAL;
     cmd->fn = fn;
     cmd->enabled = true;
     cmd->options = options;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 04/24] qmp-input: Clean up stack handling
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (2 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 03/24] qmp: Drop dead command->type Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 05/24] qapi: Consolidate QMP input visitor creation Eric Blake
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Management of the top of stack was a bit verbose; creating a
temporary variable and adding some comments makes the existing
code more legible before the next few patches improve things.
No semantic changes other than asserting that we are always
visiting a QObject, and not a NULL value.  In particular, the
check for 'name && qobject_type(qobj) == QTYPE_QDICT)' is a
bit overkill (a dict visit should always have a name); a later
patch revisits that, while this patch is only changing one
layer of indentation due to dropping 'if (qobj)'.

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

---
v15: comment improvements
v14: no change
v13: no change
v12: new patch
---
 qapi/qmp-input-visitor.c | 51 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 35 insertions(+), 16 deletions(-)

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 77cce8b..8550bc7 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -25,16 +25,23 @@

 typedef struct StackObject
 {
-    QObject *obj;
-    const QListEntry *entry;
-    GHashTable *h;
+    QObject *obj; /* Object being visited */
+
+    GHashTable *h;           /* If obj is dict: unvisited keys */
+    const QListEntry *entry; /* If obj is list: unvisited tail */
 } StackObject;

 struct QmpInputVisitor
 {
     Visitor visitor;
+
+    /* Stack of objects being visited.  stack[0] is root of visit,
+     * stack[1..] records the nesting of start_struct()/end_struct()
+     * and start_list()/end_list() pairs. */
     StackObject stack[QIV_STACK_SIZE];
     int nb_stack;
+
+    /* True to reject parse in visit_end_struct() if unvisited keys remain. */
     bool strict;
 };

@@ -47,19 +54,29 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
                                      const char *name,
                                      bool consume)
 {
-    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj;
+    StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
+    QObject *qobj = tos->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);
-            }
-            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);
+    assert(qobj);
+
+    /* If we have a name, and we're in a dictionary, then return that
+     * value. */
+    if (name && qobject_type(qobj) == QTYPE_QDICT) {
+        if (tos->h && consume) {
+            g_hash_table_remove(tos->h, name);
         }
+        return qdict_get(qobject_to_qdict(qobj), name);
     }

+    /* If we are in the middle of a list, then return the next element
+     * of the list. */
+    if (tos->entry) {
+        assert(qobject_type(qobj) == QTYPE_QLIST);
+        return qlist_entry_obj(tos->entry);
+    }
+
+    /* Otherwise, we are at the root of the visit or the start of a
+     * list, and return the object as-is. */
     return qobj;
 }

@@ -72,20 +89,22 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque)
 static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
 {
     GHashTable *h;
+    StackObject *tos = &qiv->stack[qiv->nb_stack];

+    assert(obj);
     if (qiv->nb_stack >= QIV_STACK_SIZE) {
         error_setg(errp, "An internal buffer overran");
         return;
     }

-    qiv->stack[qiv->nb_stack].obj = obj;
-    qiv->stack[qiv->nb_stack].entry = NULL;
-    qiv->stack[qiv->nb_stack].h = NULL;
+    tos->obj = obj;
+    tos->entry = NULL;
+    tos->h = NULL;

     if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
         h = g_hash_table_new(g_str_hash, g_str_equal);
         qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
-        qiv->stack[qiv->nb_stack].h = h;
+        tos->h = h;
     }

     qiv->nb_stack++;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 05/24] qapi: Consolidate QMP input visitor creation
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (3 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 04/24] qmp-input: Clean up stack handling Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 06/24] qapi: Use strict QMP input visitor in more places Eric Blake
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, Andreas Färber, Daniel P. Berrange,
	Gerd Hoffmann, Paolo Bonzini

Rather than having two separate ways to create a QMP input
visitor, where the safer approach has the more verbose name,
it is better to consolidate things into a single function
where the caller must explicitly choose whether to be strict
or to ignore excess input.  This patch is the strictly
mechanical conversion; the next patch will then audit which
uses can be made stricter.

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

---
v16: split signature change from adding strictness
v15: new patch
---
 scripts/qapi-commands.py           |  2 +-
 include/qapi/qmp-input-visitor.h   |  9 +++++++--
 qapi/qmp-input-visitor.c           | 13 ++-----------
 qmp.c                              |  2 +-
 qom/qom-qobject.c                  |  2 +-
 replay/replay-input.c              |  2 +-
 tests/test-qmp-commands.c          |  2 +-
 tests/test-qmp-input-strict.c      |  2 +-
 tests/test-qmp-input-visitor.c     |  2 +-
 tests/test-visitor-serialization.c |  2 +-
 util/qemu-sockets.c                |  2 +-
 docs/qapi-code-gen.txt             |  2 +-
 12 files changed, 19 insertions(+), 23 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index b570069..6261e44 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -115,7 +115,7 @@ def gen_marshal(name, arg_type, ret_type):

     if arg_type and arg_type.members:
         ret += mcgen('''
-    QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
+    QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
     QapiDeallocVisitor *qdv;
     Visitor *v;
     %(c_name)s arg = {0};
diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h
index 3ed499c..b0624d8 100644
--- a/include/qapi/qmp-input-visitor.h
+++ b/include/qapi/qmp-input-visitor.h
@@ -19,8 +19,13 @@

 typedef struct QmpInputVisitor QmpInputVisitor;

-QmpInputVisitor *qmp_input_visitor_new(QObject *obj);
-QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj);
+/*
+ * Return a new input visitor that converts QMP to QAPI.
+ *
+ * Set @strict to reject a parse that doesn't consume all keys of a
+ * dictionary; otherwise excess input is ignored.
+ */
+QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict);

 void qmp_input_visitor_cleanup(QmpInputVisitor *v);

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 8550bc7..c3c3271 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -356,7 +356,7 @@ void qmp_input_visitor_cleanup(QmpInputVisitor *v)
     g_free(v);
 }

-QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
+QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
 {
     QmpInputVisitor *v;

@@ -376,19 +376,10 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.type_number = qmp_input_type_number;
     v->visitor.type_any = qmp_input_type_any;
     v->visitor.optional = qmp_input_optional;
+    v->strict = strict;

     qmp_input_push(v, obj, NULL);
     qobject_incref(obj);

     return v;
 }
-
-QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj)
-{
-    QmpInputVisitor *v;
-
-    v = qmp_input_visitor_new(obj);
-    v->strict = true;
-
-    return v;
-}
diff --git a/qmp.c b/qmp.c
index 9d0953b..0cc9f3a 100644
--- a/qmp.c
+++ b/qmp.c
@@ -663,7 +663,7 @@ void qmp_object_add(const char *type, const char *id,
         }
     }

-    qiv = qmp_input_visitor_new(props);
+    qiv = qmp_input_visitor_new(props, false);
     obj = user_creatable_add_type(type, id, pdict,
                                   qmp_input_get_visitor(qiv), errp);
     qmp_input_visitor_cleanup(qiv);
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index e6b17c1..451fed6 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -22,7 +22,7 @@ void object_property_set_qobject(Object *obj, QObject *value,
                                  const char *name, Error **errp)
 {
     QmpInputVisitor *qiv;
-    qiv = qmp_input_visitor_new(value);
+    qiv = qmp_input_visitor_new(value, false);
     object_property_set(obj, qmp_input_get_visitor(qiv), name, errp);

     qmp_input_visitor_cleanup(qiv);
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 06babe0..8e8536a 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -37,7 +37,7 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)
         return NULL;
     }

-    qiv = qmp_input_visitor_new(obj);
+    qiv = qmp_input_visitor_new(obj, false);
     iv = qmp_input_get_visitor(qiv);
     visit_type_InputEvent(iv, NULL, &dst, &error_abort);
     qmp_input_visitor_cleanup(qiv);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 14a9ebb..a8d37c4 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -222,7 +222,7 @@ static void test_dealloc_partial(void)
         ud2_dict = qdict_new();
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));

-        qiv = qmp_input_visitor_new(QOBJECT(ud2_dict));
+        qiv = qmp_input_visitor_new(QOBJECT(ud2_dict), false);
         visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err);
         qmp_input_visitor_cleanup(qiv);
         QDECREF(ud2_dict);
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index d5f80ec..2b053a2 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -55,7 +55,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data,
     data->obj = qobject_from_jsonv(json_string, ap);
     g_assert(data->obj);

-    data->qiv = qmp_input_visitor_new_strict(data->obj);
+    data->qiv = qmp_input_visitor_new(data->obj, true);
     g_assert(data->qiv);

     v = qmp_input_get_visitor(data->qiv);
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 80527eb..c039806 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -51,7 +51,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
     data->obj = qobject_from_jsonv(json_string, ap);
     g_assert(data->obj);

-    data->qiv = qmp_input_visitor_new(data->obj);
+    data->qiv = qmp_input_visitor_new(data->obj, false);
     g_assert(data->qiv);

     v = qmp_input_get_visitor(data->qiv);
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 9adbc30..2caac2b 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1038,7 +1038,7 @@ static void qmp_deserialize(void **native_out, void *datap,
     obj = qobject_from_json(qstring_get_str(output_json));

     QDECREF(output_json);
-    d->qiv = qmp_input_visitor_new(obj);
+    d->qiv = qmp_input_visitor_new(obj, false);
     qobject_decref(obj_orig);
     qobject_decref(obj);
     visit(qmp_input_get_visitor(d->qiv), native_out, errp);
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 0d53691..aab5344 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1145,7 +1145,7 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,
         return;
     }

-    qiv = qmp_input_visitor_new(obj);
+    qiv = qmp_input_visitor_new(obj, false);
     iv = qmp_input_get_visitor(qiv);
     visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
     qmp_input_visitor_cleanup(qiv);
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 0e4baff..4a917f9 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -996,7 +996,7 @@ Example:
     {
         Error *err = NULL;
         UserDefOne *retval;
-        QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
+        QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
         QapiDeallocVisitor *qdv;
         Visitor *v;
         UserDefOneList *arg1 = NULL;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 06/24] qapi: Use strict QMP input visitor in more places
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (4 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 05/24] qapi: Consolidate QMP input visitor creation Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 07/24] qmp-input: Don't consume input when checking has_member Eric Blake
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Andreas Färber, Michael Roth, Daniel P. Berrange,
	Gerd Hoffmann, Paolo Bonzini

The following uses of a QMP input visitor should be strict
(that is, excess keys in QDict input should be flagged if not
converted to QAPI):

- Testsuite code unrelated to explicitly testing non-strict
mode (test-qmp-commands, test-visitor-serialization); since
we want more code to be strict by default, having more tests
of strict mode doesn't hurt

- Code used for cloning QAPI objects (replay-input.c,
qemu-sockets.c); we are reparsing a QObject just barely
produced by the qmp output visitor and which therefore should
not have any garbage, so while it is extra work to be strict,
it validates that our clone is correct [note that a later patch
series will simplify these two uses by creating an actual
clone visitor that is much more efficient than a
generate/reparse cycle]

- qmp_object_add(), which calls into user_creatable_add_type().
Since command line parsing for '-object' uses the same
user_creatable_add_type() through the OptsVisitor, and that is
always strict, we want to ensure that any nested dictionaries
would be treated the same in QMP and from the command line (I
don't actually know if such nested dictionaries exist).  Note
that on this code change, strictness only matters for nested
dictionaries (if even possible), since we already flag excess
input at the top level during an earlier object_property_set()
on an unknown key, whether from QemuOpts:

$ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio -object secret,id=sec0,data=letmein,format=raw,foo=bar
qemu-system-x86_64: -object secret,id=sec0,data=letmein,format=raw,foo=bar: Property '.foo' not found

or from QMP:

$ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio
{"QMP": {"version": {"qemu": {"micro": 93, "minor": 5, "major": 2}, "package": ""}, "capabilities": []}}
{"execute":"qmp_capabilities"}
{"return": {}}
{"execute":"object-add","arguments":{"qom-type":"secret","id":"sec0","props":{"format":"raw","data":"letmein","foo":"bar"}}}
{"error": {"class": "GenericError", "desc": "Property '.foo' not found"}}

The only remaining uses of non-strict input visits are:

- QMP 'qom-set' (which eventually executes
object_property_set_qobject()) - mark it as something to revisit
in the future (I didn't want to spend any more time on this patch
auditing if we have any QOM dictionary properties that might be
impacted, and couldn't easily prove whether this code path is
shared with anything else).

- test-qmp-input-visitor: explicit tests of non-strict mode. If
we later get rid of users that don't need strictness, then this
test should be merged with test-qmp-input-strict

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

---
v16: split signature change from adding strictness, better commit message
v15: new patch
---
 qmp.c                              | 2 +-
 qom/qom-qobject.c                  | 1 +
 replay/replay-input.c              | 2 +-
 tests/test-qmp-commands.c          | 2 +-
 tests/test-visitor-serialization.c | 2 +-
 util/qemu-sockets.c                | 2 +-
 6 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/qmp.c b/qmp.c
index 0cc9f3a..e784a67 100644
--- a/qmp.c
+++ b/qmp.c
@@ -663,7 +663,7 @@ void qmp_object_add(const char *type, const char *id,
         }
     }

-    qiv = qmp_input_visitor_new(props, false);
+    qiv = qmp_input_visitor_new(props, true);
     obj = user_creatable_add_type(type, id, pdict,
                                   qmp_input_get_visitor(qiv), errp);
     qmp_input_visitor_cleanup(qiv);
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index 451fed6..b66088d 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -22,6 +22,7 @@ void object_property_set_qobject(Object *obj, QObject *value,
                                  const char *name, Error **errp)
 {
     QmpInputVisitor *qiv;
+    /* TODO: Should we reject, rather than ignore, excess input? */
     qiv = qmp_input_visitor_new(value, false);
     object_property_set(obj, qmp_input_get_visitor(qiv), name, errp);

diff --git a/replay/replay-input.c b/replay/replay-input.c
index 8e8536a..03e99d5 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -37,7 +37,7 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)
         return NULL;
     }

-    qiv = qmp_input_visitor_new(obj, false);
+    qiv = qmp_input_visitor_new(obj, true);
     iv = qmp_input_get_visitor(qiv);
     visit_type_InputEvent(iv, NULL, &dst, &error_abort);
     qmp_input_visitor_cleanup(qiv);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index a8d37c4..597fb44 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -222,7 +222,7 @@ static void test_dealloc_partial(void)
         ud2_dict = qdict_new();
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));

-        qiv = qmp_input_visitor_new(QOBJECT(ud2_dict), false);
+        qiv = qmp_input_visitor_new(QOBJECT(ud2_dict), true);
         visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err);
         qmp_input_visitor_cleanup(qiv);
         QDECREF(ud2_dict);
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 2caac2b..7b14b5a 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1038,7 +1038,7 @@ static void qmp_deserialize(void **native_out, void *datap,
     obj = qobject_from_json(qstring_get_str(output_json));

     QDECREF(output_json);
-    d->qiv = qmp_input_visitor_new(obj, false);
+    d->qiv = qmp_input_visitor_new(obj, true);
     qobject_decref(obj_orig);
     qobject_decref(obj);
     visit(qmp_input_get_visitor(d->qiv), native_out, errp);
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index aab5344..2a2c524 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1145,7 +1145,7 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,
         return;
     }

-    qiv = qmp_input_visitor_new(obj, false);
+    qiv = qmp_input_visitor_new(obj, true);
     iv = qmp_input_get_visitor(qiv);
     visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
     qmp_input_visitor_cleanup(qiv);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 07/24] qmp-input: Don't consume input when checking has_member
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (5 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 06/24] qapi: Use strict QMP input visitor in more places Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 08/24] qapi-commands: Wrap argument visit in visit_start_struct Eric Blake
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Commit e8316d7 mistakenly passed consume=true within
qmp_input_optional() when checking if an optional member was
present, but the mistake was silently ignored since the code
happily let us extract a member more than once.  Fix
qmp_input_optional() to not consume anything, then tighten up
the input visitor to ensure that a member is consumed exactly
once (all generated code follows this pattern; and the new
assert will catch any hand-written code that tries to visit
the same key more than once).

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

---
v15: change variable name, improve commit message
v14: no change
v13: no change
v12: new patch
---
 qapi/qmp-input-visitor.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index c3c3271..cae6387 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -56,16 +56,19 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
 {
     StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
     QObject *qobj = tos->obj;
+    QObject *ret;

     assert(qobj);

     /* If we have a name, and we're in a dictionary, then return that
      * value. */
     if (name && qobject_type(qobj) == QTYPE_QDICT) {
-        if (tos->h && consume) {
-            g_hash_table_remove(tos->h, name);
+        ret = qdict_get(qobject_to_qdict(qobj), name);
+        if (tos->h && consume && ret) {
+            bool removed = g_hash_table_remove(tos->h, name);
+            assert(removed);
         }
-        return qdict_get(qobject_to_qdict(qobj), name);
+        return ret;
     }

     /* If we are in the middle of a list, then return the next element
@@ -335,7 +338,7 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
 {
     QmpInputVisitor *qiv = to_qiv(v);
-    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QObject *qobj = qmp_input_get_object(qiv, name, false);

     if (!qobj) {
         *present = false;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 08/24] qapi-commands: Wrap argument visit in visit_start_struct
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (6 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 07/24] qmp-input: Don't consume input when checking has_member Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 09/24] qom: Wrap prop " Eric Blake
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

The qmp-input visitor was allowing callers to play rather fast
and loose: when visiting a QDict, you could grab members of the
root dictionary without first pushing into the dict; among the
culprit callers was the generated marshal code on the 'arguments'
dictionary of a QMP command.  But we are about to tighten the
input visitor, at which point the generated marshal code MUST
follow the same paradigms as everyone else, of pushing into the
struct before grabbing its keys.

Generated code grows as follows:

|@@ -515,7 +641,12 @@ void qmp_marshal_blockdev_backup(QDict *
|     BlockdevBackup arg = {0};
|
|     v = qmp_input_get_visitor(qiv);
|+    visit_start_struct(v, NULL, NULL, 0, &err);
|+    if (err) {
|+        goto out;
|+    }
|     visit_type_BlockdevBackup_members(v, &arg, &err);
|+    visit_end_struct(v, err ? NULL : &err);
|     if (err) {
|         goto out;
|     }
|@@ -527,7 +715,9 @@ out:
|     qmp_input_visitor_cleanup(qiv);
|     qdv = qapi_dealloc_visitor_new();
|     v = qapi_dealloc_get_visitor(qdv);
|+    visit_start_struct(v, NULL, NULL, 0, NULL);
|     visit_type_BlockdevBackup_members(v, &arg, NULL);
|+    visit_end_struct(v, NULL);
|     qapi_dealloc_visitor_cleanup(qdv);
| }

The use of 'err ? NULL : &err' is temporary; a later patch will
clean that up when it splits visit_end_struct().

Prior to this patch, the fact that there was no final
visit_end_struct() meant that even though we are using a strict
input visit, the marshalling code was not detecting excess input
at the top level (only in nested levels).  Fortunately, we have
code in monitor.c:qmp_check_client_args() that also checks for
no excess arguments at the top level.  But as the generated code
is more compact than the manual check, a later patch will clean
up monitor.c to drop the redundancy added here.

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

---
v15: hoist earlier in series, improve commit message, update docs
v14: rebase to master context
v13: rebase to earlier patches
v12: new patch
---
 scripts/qapi-commands.py | 7 +++++++
 docs/qapi-code-gen.txt   | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 6261e44..04549fa 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -121,7 +121,12 @@ def gen_marshal(name, arg_type, ret_type):
     %(c_name)s arg = {0};

     v = qmp_input_get_visitor(qiv);
+    visit_start_struct(v, NULL, NULL, 0, &err);
+    if (err) {
+        goto out;
+    }
     visit_type_%(c_name)s_members(v, &arg, &err);
+    visit_end_struct(v, err ? NULL : &err);
     if (err) {
         goto out;
     }
@@ -150,7 +155,9 @@ out:
     qmp_input_visitor_cleanup(qiv);
     qdv = qapi_dealloc_visitor_new();
     v = qapi_dealloc_get_visitor(qdv);
+    visit_start_struct(v, NULL, NULL, 0, NULL);
     visit_type_%(c_name)s_members(v, &arg, NULL);
+    visit_end_struct(v, NULL);
     qapi_dealloc_visitor_cleanup(qdv);
 ''',
                      c_name=arg_type.c_name())
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 4a917f9..b4ae1be 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1002,7 +1002,12 @@ Example:
         UserDefOneList *arg1 = NULL;

         v = qmp_input_get_visitor(qiv);
+        visit_start_struct(v, NULL, NULL, 0, &err);
+        if (err) {
+            goto out;
+        }
         visit_type_UserDefOneList(v, "arg1", &arg1, &err);
+        visit_end_struct(v, err ? NULL : &err);
         if (err) {
             goto out;
         }
@@ -1019,7 +1024,9 @@ Example:
         qmp_input_visitor_cleanup(qiv);
         qdv = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(qdv);
+        visit_start_struct(v, NULL, NULL, 0, NULL);
         visit_type_UserDefOneList(v, "arg1", &arg1, NULL);
+        visit_end_struct(v, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 09/24] qom: Wrap prop visit in visit_start_struct
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (7 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 08/24] qapi-commands: Wrap argument visit in visit_start_struct Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 10/24] qmp-input: Require struct push to visit members of top dict Eric Blake
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Andreas Färber

The qmp-input visitor was allowing callers to play rather fast
and loose: when visiting a QDict, you could grab members of the
root dictionary without first pushing into the dict; the final
such culprit was the QOM code for converting to and from object
properties.  But we are about to tighten the input visitor, at
which point user_creatable_add_type() as called with a QMP input
visitor via qmp_object_add() MUST follow the same paradigms as
everyone else, of pushing into the struct before grabbing its
keys.

The use of 'err ? NULL : &err' is temporary; a later patch will
clean that up when it splits visit_end_struct().

Furthermore, note that both callers always pass qdict, so we can
convert the conditional into an assert and reduce indentation.

The change has no impact to the testsuite now, but is required to
avoid a failure in tests/test-netfilter once qmp-input is made
stricter to detect inconsistent 'name' arguments on the root visit.

Since user_creatable_add_type() is also called with OptsVisitor
through user_creatable_add_opts(), we must also check that there
is no negative impact there; both pre- and post-patch, we see:

$ ./x86_64-softmmu/qemu-system-x86_64 -nographic -nodefaults -qmp stdio -object secret,id=sec0,data=letmein,format=raw,foo=bar
qemu-system-x86_64: -object secret,id=sec0,data=letmein,format=raw,foo=bar: Property '.foo' not found

That is, the only new checking that the new visit_end_struct() can
perform is for excess input, but we already catch excess input
earlier in object_property_set().

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

---
v16: turn qdict conditional into assertion
v15: hoist earlier in series, improve commit message
v14: no change
v13: no change
v12: new patch
---
 qom/object_interfaces.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 3931890..cad5542 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -118,15 +118,22 @@ Object *user_creatable_add_type(const char *type, const char *id,
         return NULL;
     }

+    assert(qdict);
     obj = object_new(type);
-    if (qdict) {
-        for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
-            object_property_set(obj, v, e->key, &local_err);
-            if (local_err) {
-                goto out;
-            }
+    visit_start_struct(v, NULL, NULL, 0, &local_err);
+    if (local_err) {
+        goto out;
+    }
+    for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
+        object_property_set(obj, v, e->key, &local_err);
+        if (local_err) {
+            break;
         }
     }
+    visit_end_struct(v, local_err ? NULL : &local_err);
+    if (local_err) {
+        goto out;
+    }

     object_property_add_child(object_get_objects_root(),
                               id, obj, &local_err);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 10/24] qmp-input: Require struct push to visit members of top dict
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (8 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 09/24] qom: Wrap prop " Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced Eric Blake
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Don't embed the root of the visit into the stack of current
containers being visited.  That way, we no longer get confused
on whether the first visit of a dictionary is to the dictionary
itself or to one of the members of the dictionary, based on
whether the caller passed name=NULL; and makes the QMP Input
visitor like other visitors where the value of 'name' is now
ignored on the root visit.  (We may someday want to revisit
the rules on what 'name' should be on a top-level visit,
rather than just ignoring it; but that would be the topic of
another patch).

An audit of all qmp_input_visitor_new() call sites shows that
there were only two places where callers had previously been
visiting to a QDict with a non-NULL name to bypass a call to
visit_start_struct(), and those were fixed in prior patches.

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

---
v16: drop dead initializer
v15: hoist earlier in series, improve variable naming
v14: no change
v13: no change
v12: new patch
---
 qapi/qmp-input-visitor.c | 43 +++++++++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index cae6387..f158d57 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -35,9 +35,11 @@ struct QmpInputVisitor
 {
     Visitor visitor;

-    /* Stack of objects being visited.  stack[0] is root of visit,
-     * stack[1..] records the nesting of start_struct()/end_struct()
-     * and start_list()/end_list() pairs. */
+    /* Root of visit at visitor creation. */
+    QObject *root;
+
+    /* Stack of objects being visited (all entries will be either
+     * QDict or QList). */
     StackObject stack[QIV_STACK_SIZE];
     int nb_stack;

@@ -54,33 +56,34 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
                                      const char *name,
                                      bool consume)
 {
-    StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
-    QObject *qobj = tos->obj;
+    StackObject *tos;
+    QObject *qobj;
     QObject *ret;

+    if (!qiv->nb_stack) {
+        /* Starting at root, name is ignored. */
+        return qiv->root;
+    }
+
+    /* We are in a container; find the next element. */
+    tos = &qiv->stack[qiv->nb_stack - 1];
+    qobj = tos->obj;
     assert(qobj);

-    /* If we have a name, and we're in a dictionary, then return that
-     * value. */
-    if (name && qobject_type(qobj) == QTYPE_QDICT) {
+    if (qobject_type(qobj) == QTYPE_QDICT) {
+        assert(name);
         ret = qdict_get(qobject_to_qdict(qobj), name);
         if (tos->h && consume && ret) {
             bool removed = g_hash_table_remove(tos->h, name);
             assert(removed);
         }
-        return ret;
-    }
-
-    /* If we are in the middle of a list, then return the next element
-     * of the list. */
-    if (tos->entry) {
+    } else {
         assert(qobject_type(qobj) == QTYPE_QLIST);
-        return qlist_entry_obj(tos->entry);
+        assert(!name);
+        ret = qlist_entry_obj(tos->entry);
     }

-    /* Otherwise, we are at the root of the visit or the start of a
-     * list, and return the object as-is. */
-    return qobj;
+    return ret;
 }

 static void qdict_add_key(const char *key, QObject *obj, void *opaque)
@@ -355,7 +358,7 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v)

 void qmp_input_visitor_cleanup(QmpInputVisitor *v)
 {
-    qobject_decref(v->stack[0].obj);
+    qobject_decref(v->root);
     g_free(v);
 }

@@ -381,7 +384,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->visitor.optional = qmp_input_optional;
     v->strict = strict;

-    qmp_input_push(v, obj, NULL);
+    v->root = obj;
     qobject_incref(obj);

     return v;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (9 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 10/24] qmp-input: Require struct push to visit members of top dict Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-29  8:50   ` Markus Armbruster
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 12/24] qapi: Document visitor interfaces, add assertions Eric Blake
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

In the QMP input visitor, visiting a list traverses two objects:
the QAPI GenericList of the caller (which gets advanced in
visit_next_list() regardless of this patch), and the QList input
that we are converting to QAPI.  For consistency with QDict
visits, we want to consume elements from the input QList during
the visit_type_FOO() for the list element; that is, we want ALL
the code for consuming an input to live in qmp_input_get_object(),
rather than having it split according to whether we are visiting
a dict or a list.  Making qmp_input_get_object() the common point
of consumption will make it easier for a later patch to refactor
visit_start_list() to cover the GenericList * head of a QAPI list,
and in turn will get rid of the 'first' flag (which lived in
qmp_input_next_list() pre-patch, and is hoisted to StackObject
by this patch).

This patch is therefore shifting the post-condition use of
'entry' from:

        start_list next_list type_ELT ... next_list type_ELT next_list end_list
 visit                       1st elt                last elt
 entry  NULL       1st elt   1st elt      last elt  last elt NULL      gone

where type_ELT() returns (entry ? entry : 1st elt) and next_list() steps
entry

to this usage:

        start_list next_list type_ELT ... next_list type_ELT next_list end_list
 visit                       1st elt                last elt
 entry  1st elt    1nd elt   2nd elt      last elt  NULL     NULL      gone

where type_ELT() steps entry and returns the old entry, and next_list()
leaves entry alone.

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

---
v16: comment and formatting tweak
v15: rebase, improve commit message, hoist root handling earlier
so that this patch is more pinpointed
v14: no change
v13: no change
v12: new patch
---
 qapi/qmp-input-visitor.c | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index f158d57..5159833 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -29,6 +29,7 @@ typedef struct StackObject

     GHashTable *h;           /* If obj is dict: unvisited keys */
     const QListEntry *entry; /* If obj is list: unvisited tail */
+    bool first;              /* If obj is list: next_list() not yet called? */
 } StackObject;

 struct QmpInputVisitor
@@ -80,7 +81,11 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
     } else {
         assert(qobject_type(qobj) == QTYPE_QLIST);
         assert(!name);
+        assert(!tos->first);
         ret = qlist_entry_obj(tos->entry);
+        if (consume) {
+            tos->entry = qlist_next(tos->entry);
+        }
     }

     return ret;
@@ -104,13 +109,16 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
     }

     tos->obj = obj;
-    tos->entry = NULL;
-    tos->h = NULL;
+    assert(!tos->h);
+    assert(!tos->entry);

     if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
         h = g_hash_table_new(g_str_hash, g_str_equal);
         qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
         tos->h = h;
+    } else if (qobject_type(obj) == QTYPE_QLIST) {
+        tos->entry = qlist_first(qobject_to_qlist(obj));
+        tos->first = true;
     }

     qiv->nb_stack++;
@@ -119,10 +127,11 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)

 static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
 {
+    StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
     assert(qiv->nb_stack > 0);

     if (qiv->strict) {
-        GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
+        GHashTable *const top_ht = tos->h;
         if (top_ht) {
             GHashTableIter iter;
             const char *key;
@@ -133,6 +142,7 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
             }
             g_hash_table_unref(top_ht);
         }
+        tos->h = NULL;
     }

     qiv->nb_stack--;
@@ -192,23 +202,15 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
     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(size);
-    if (first) {
+    if (so->first) {
         *list = entry;
+        so->first = false;
     } else {
         (*list)->next = entry;
     }
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 12/24] qapi: Document visitor interfaces, add assertions
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (10 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-05-04 14:05   ` [Qemu-devel] [PATCH] fixup! " Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 13/24] tests: Add check-qnull Eric Blake
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

The visitor interface for mapping between QObject/QemuOpts/string
and QAPI is scandalously under-documented, making changes to visitor
core, individual visitors, and users of visitors difficult to
coordinate.  Among other questions: when is it safe to pass NULL,
vs. when a string must be provided; which visitors implement which
callbacks; the difference between concrete and virtual visits.

Correct this by retrofitting proper contracts, and document where some
of the interface warts remain (for example, we may want to modify
visit_end_* to require the same 'obj' as the visit_start counterpart,
so the dealloc visitor can be simplified).  Later patches in this
series will tackle some, but not all, of these warts.

Add assertions to (partially) enforce the contract.  Some of these
were only made possible by recent cleanup commits.

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

---
v16: final wording cleanups
v15: wording tweaks based on review, rebase to some assertions going
in earlier
v14: rebase to master context
v13: minor wording tweaks for consistency
v12: major rework based on Markus' comments, drop R-b
[no v10, v11]
v9: no change
v8: rebase to 'name' motion
v7: retitle; more wording changes, add asserts to enforce the
wording, place later in series to rebase on fixes that would
otherwise trip the new assertions
v6: mention that input visitors blindly assign over *obj; wording
improvements
---
 include/qapi/visitor.h               | 446 +++++++++++++++++++++++++++++++++--
 include/qapi/visitor-impl.h          |  44 +++-
 include/qapi/dealloc-visitor.h       |   5 +
 include/qapi/opts-visitor.h          |   3 +
 include/qapi/string-input-visitor.h  |   4 +
 include/qapi/string-output-visitor.h |   4 +
 qapi/qapi-visit-core.c               |  18 +-
 7 files changed, 495 insertions(+), 29 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 690589f..0bb2fe1 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -16,8 +16,195 @@

 #include "qapi/qmp/qobject.h"

+/*
+ * The QAPI schema defines both a set of C data types, and a QMP wire
+ * format.  QAPI objects can contain references to other QAPI objects,
+ * resulting in a directed acyclic graph.  QAPI also generates visitor
+ * functions to walk these graphs.  This file represents the interface
+ * for doing work at each node of a QAPI graph; it can also be used
+ * for a virtual walk, where there is no actual QAPI C struct.
+ *
+ * There are three kinds of visitor classes: input visitors (QMP,
+ * string, and QemuOpts) parse an external representation and build
+ * the corresponding QAPI graph, output visitors (QMP and string) take
+ * a completed QAPI graph and generate an external representation, and
+ * the dealloc visitor can take a QAPI graph (possibly partially
+ * constructed) and recursively free its resources.  While the dealloc
+ * and QMP input/output visitors are general, the string and QemuOpts
+ * visitors have some implementation limitations; see the
+ * documentation for each visitor for more details on what it
+ * supports.  Also, see visitor-impl.h for the callback contracts
+ * implemented by each visitor, and docs/qapi-code-gen.txt for more
+ * about the QAPI code generator.
+ *
+ * All QAPI types have a corresponding function with a signature
+ * roughly compatible with this:
+ *
+ * void visit_type_FOO(Visitor *v, const char *name, T obj, Error **errp);
+ *
+ * where T is FOO for scalar types, and FOO * otherwise.  The scalar
+ * visitors are declared here; the remaining visitors are generated in
+ * qapi-visit.h.
+ *
+ * The @name parameter of visit_type_FOO() describes the relation
+ * between this QAPI value and its parent container.  When visiting
+ * the root of a tree, @name is ignored; when visiting a member of an
+ * object, @name is the key associated with the value; and when
+ * visiting a member of a list, @name is NULL.
+ *
+ * FIXME: Clients must pass NULL for @name when visiting a member of a
+ * list, but this leads to poor error messages; it might be nicer to
+ * require a non-NULL name such as "key.0" for '{ "key": [ "value" ]
+ * }' if an error is encountered on "value" (or to have the visitor
+ * core auto-generate the nicer name).
+ *
+ * The visit_type_FOO() functions expect a non-null @obj argument;
+ * they allocate *@obj during input visits, leave it unchanged on
+ * output visits, and recursively free any resources during a dealloc
+ * visit.  Each function also takes the customary @errp argument (see
+ * qapi/error.h for details), for reporting any errors (such as if a
+ * member @name is not present, or is present but not the specified
+ * type).
+ *
+ * FIXME: At present, visit_type_FOO() is an awkward interface: input
+ * visitors may allocate an incomplete *@obj even when reporting an
+ * error, but using an output visitor with an incomplete object has
+ * undefined behavior.  To avoid a memory leak, callers must use
+ * qapi_free_FOO() even on error (this uses the dealloc visitor, and
+ * safely handles an incomplete object).
+ *
+ * For the QAPI object types (structs, unions, and alternates), there
+ * is an additional generated function in qapi-visit.h compatible
+ * with:
+ *
+ * void visit_type_FOO_members(Visitor *v, FOO *obj, Error **errp);
+ *
+ * for visiting the members of a type without also allocating the QAPI
+ * struct.
+ *
+ * Additionally, in qapi-types.h, all QAPI pointer types (structs,
+ * unions, alternates, and lists) have a generated function compatible
+ * with:
+ *
+ * void qapi_free_FOO(FOO *obj);
+ *
+ * which behaves like free() in that @obj may be NULL.  Because of
+ * these functions, the dealloc visitor is seldom used directly
+ * outside of generated code.  QAPI types can also inherit from a base
+ * class; when this happens, a function is generated for easily going
+ * from the derived type to the base type:
+ *
+ * BASE *qapi_CHILD_base(CHILD *obj);
+ *
+ * For a real QAPI struct, typical input usage involves:
+ *
+ * <example>
+ *  Foo *f;
+ *  Error *err = NULL;
+ *  Visitor *v;
+ *
+ *  v = ...obtain input visitor...
+ *  visit_type_Foo(v, NULL, &f, &err);
+ *  if (err) {
+ *      qapi_free_Foo(f);
+ *      ...handle error...
+ *  } else {
+ *      ...use f...
+ *  }
+ *  ...clean up v...
+ *  qapi_free_Foo(f);
+ * </example>
+ *
+ * For a list, it is:
+ * <example>
+ *  FooList *l;
+ *  Error *err = NULL;
+ *  Visitor *v;
+ *
+ *  v = ...obtain input visitor...
+ *  visit_type_FooList(v, NULL, &l, &err);
+ *  if (err) {
+ *      qapi_free_FooList(l);
+ *      ...handle error...
+ *  } else {
+ *      for ( ; l; l = l->next) {
+ *          ...use l->value...
+ *      }
+ *  }
+ *  ...clean up v...
+ *  qapi_free_FooList(l);
+ * </example>
+ *
+ * Similarly, typical output usage is:
+ *
+ * <example>
+ *  Foo *f = ...obtain populated object...
+ *  Error *err = NULL;
+ *  Visitor *v;
+ *
+ *  v = ...obtain output visitor...
+ *  visit_type_Foo(v, NULL, &f, &err);
+ *  if (err) {
+ *      ...handle error...
+ *  }
+ *  ...clean up v...
+ * </example>
+ *
+ * When visiting a real QAPI struct, this file provides several
+ * helpers that rely on in-tree information to control the walk:
+ * visit_optional() for the 'has_member' field associated with
+ * optional 'member' in the C struct; and visit_next_list() for
+ * advancing through a FooList linked list.  Only the generated
+ * visit_type functions need to use these helpers.
+ *
+ * It is also possible to use the visitors to do a virtual walk, where
+ * no actual QAPI struct is present.  In this situation, decisions
+ * about what needs to be walked are made by the calling code, and
+ * structured visits are split between pairs of start and end methods
+ * (where the end method must be called if the start function
+ * succeeded, even if an intermediate visit encounters an error).
+ * Thus, a virtual walk corresponding to '{ "list": [1, 2] }' looks
+ * like:
+ *
+ * <example>
+ *  Visitor *v;
+ *  Error *err = NULL;
+ *  int value;
+ *
+ *  v = ...obtain visitor...
+ *  visit_start_struct(v, NULL, NULL, 0, &err);
+ *  if (err) {
+ *      goto outobj;
+ *  }
+ *  visit_start_list(v, "list", &err);
+ *  if (err) {
+ *      goto outobj;
+ *  }
+ *  value = 1;
+ *  visit_type_int(v, NULL, &value, &err);
+ *  if (err) {
+ *      goto outlist;
+ *  }
+ *  value = 2;
+ *  visit_type_int(v, NULL, &value, &err);
+ *  if (err) {
+ *      goto outlist;
+ *  }
+ * outlist:
+ *  visit_end_list(v);
+ * outobj:
+ *  error_propagate(errp, err);
+ *  err = NULL;
+ *  visit_end_struct(v, &err);
+ *  error_propagate(errp, err);
+ *  ...clean up v...
+ * </example>
+ */
+
+/*** Useful types ***/
+
 /* This struct is layout-compatible with all other *List structs
- * created by the qapi generator.  It is used as a typical
+ * created by the QAPI generator.  It is used as a typical
  * singly-linked list. */
 typedef struct GenericList {
     struct GenericList *next;
@@ -25,35 +212,126 @@ typedef struct GenericList {
 } GenericList;

 /* This struct is layout-compatible with all Alternate types
- * created by the qapi generator. */
+ * created by the QAPI generator. */
 typedef struct GenericAlternate {
     QType type;
     char padding[];
 } GenericAlternate;

+/*** Visiting structures ***/
+
+/*
+ * Start visiting an object @obj (struct or union).
+ *
+ * @name expresses the relationship of this object to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL for a real walk, in which case @size
+ * determines how much memory an input visitor will allocate into
+ * *@obj.  @obj may also be NULL for a virtual walk, in which case
+ * @size is ignored.
+ *
+ * @errp obeys typical error usage, and reports failures such as a
+ * member @name is not present, or present but not an object.  On
+ * error, input visitors set *@obj to NULL.
+ *
+ * After visit_start_struct() succeeds, the caller may visit its
+ * members one after the other, passing the member's name and address
+ * within the struct.  Finally, visit_end_struct() needs to be called
+ * to clean up, even if intermediate visits fail.  See the examples
+ * above.
+ *
+ * FIXME Should this be named visit_start_object, since it is also
+ * used for QAPI unions, and maps to JSON objects?
+ */
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp);
+
+/*
+ * Complete an object visit started earlier.
+ *
+ * @errp obeys typical error usage, and reports failures such as
+ * unparsed keys remaining in the input stream.
+ *
+ * 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.  Destroying the visitor early
+ * behaves as if this was implicitly called.
+ */
 void visit_end_struct(Visitor *v, Error **errp);

+
+/*** Visiting lists ***/
+
+/*
+ * Start visiting a list.
+ *
+ * @name expresses the relationship of this list to its parent
+ * container; see the general description of @name above.
+ *
+ * @errp obeys typical error usage, and reports failures such as a
+ * member @name is not present, or present but not a list.
+ *
+ * After visit_start_list() succeeds, the caller may visit its members
+ * one after the other.  A real visit uses visit_next_list() for
+ * traversing the linked list, while a virtual visit uses other means.
+ * For each list element, call the appropriate visit_type_FOO() with
+ * name set to NULL and obj set to the address of the value member of
+ * the list element.  Finally, visit_end_list() needs to be called to
+ * clean up, even if intermediate visits fail.  See the examples
+ * above.
+ */
 void visit_start_list(Visitor *v, const char *name, Error **errp);
+
+/*
+ * Iterate over a GenericList during a non-virtual list visit.
+ *
+ * @size represents the size of a linked list node (at least
+ * sizeof(GenericList)).
+ *
+ * @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.  Should be called in a loop until a
+ * NULL return or error occurs; for each non-NULL return, the caller
+ * then calls the appropriate visit_type_*() for the element type
+ * of the list, with that function's name parameter set to NULL and
+ * obj set to the address of (*@list)->value.
+ *
+ * FIXME: This interface is awkward; it requires all callbacks to
+ * track whether it is the first or a subsequent call.  A better
+ * interface would pass the head of the list through
+ * visit_start_list().
+ */
 GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size);
+
+/*
+ * Complete a list visit 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.  Destroying the visitor early
+ * behaves as if this was implicitly called.
+ */
 void visit_end_list(Visitor *v);

+
+/*** Visiting alternates ***/
+
 /*
- * Start the visit of an alternate @obj with the given @size.
+ * Start the visit of an alternate @obj.
  *
- * @name specifies the relationship to the containing struct (ignored
- * for a top level visit, the name of the key if this alternate is
- * part of an object, or NULL if this alternate is part of a list).
+ * @name expresses the relationship of this alternate to its parent
+ * container; see the general description of @name above.
  *
- * @obj must not be NULL. Input visitors will allocate @obj and
- * determine the qtype of the next thing to be visited, stored in
- * (*@obj)->type.  Other visitors will leave @obj unchanged.
+ * @obj must not be NULL. Input visitors use @size to determine how
+ * much memory to allocate into *@obj, then determine the qtype of the
+ * next thing to be visited, stored in (*@obj)->type.  Other visitors
+ * will leave @obj unchanged.
  *
  * If @promote_int, treat integers as QTYPE_FLOAT.
  *
- * If successful, this must be paired with visit_end_alternate(), even
- * if visiting the contents of the alternate fails.
+ * If successful, this must be paired with visit_end_alternate() to
+ * clean up, even if visiting the contents of the alternate fails.
  */
 void visit_start_alternate(Visitor *v, const char *name,
                            GenericAlternate **obj, size_t size,
@@ -62,27 +340,48 @@ void visit_start_alternate(Visitor *v, const char *name,
 /*
  * Finish visiting an alternate type.
  *
- * Must be called after a successful visit_start_alternate(), even if
- * an error occurred in the meantime.
+ * Must be called after any successful use of visit_start_alternate(),
+ * even if intermediate processing was skipped due to errors, to allow
+ * the backend to release any resources.  Destroying the visitor early
+ * behaves as if this was implicitly called.
  *
  * TODO: Should all the visit_end_* interfaces take obj parameter, so
  * that dealloc visitor need not track what was passed in visit_start?
  */
 void visit_end_alternate(Visitor *v);

-/**
- * Check if an optional member @name of an object needs visiting.
- * For input visitors, set *@present according to whether the
- * corresponding visit_type_*() needs calling; for other visitors,
- * leave *@present unchanged.  Return *@present for convenience.
+
+/*** Other helpers ***/
+
+/*
+ * Does optional struct member @name need visiting?
+ *
+ * @name must not be NULL.  This function is only useful between
+ * visit_start_struct() and visit_end_struct(), since only objects
+ * have optional keys.
+ *
+ * @present points to the address of the optional member's has_ flag.
+ *
+ * Input visitors set *@present according to input; other visitors
+ * leave it unchanged.  In either case, return *@present for
+ * convenience.
  */
 bool visit_optional(Visitor *v, const char *name, bool *present);

 /*
  * Visit an enum value.
  *
- * @strings expresses the mapping between C enum values and QAPI enum
- * names; it should be the ENUM_lookup array from visit-types.h.
+ * @name expresses the relationship of this enum to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL.  Input visitors parse input and set *@obj to
+ * the enumeration value, leaving @obj unchanged on error; other
+ * visitors use *@obj but leave it unchanged.
+ *
+ * Currently, all input visitors parse text input, and all output
+ * visitors produce text output.  The mapping between enumeration
+ * values and strings is done by the visitor core, using @strings; it
+ * should be the ENUM_lookup array from visit-types.h.
  *
  * May call visit_type_str() under the hood, and the enum visit may
  * fail even if the corresponding string visit succeeded; this implies
@@ -91,28 +390,135 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
 void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp);

+/*** Visiting built-in types ***/
+
+/*
+ * Visit an integer value.
+ *
+ * @name expresses the relationship of this integer to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL.  Input visitors set *@obj to the value;
+ * other visitors will leave *@obj unchanged.
+ */
 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
+
+/*
+ * Visit a uint8_t value.
+ * Like visit_type_int(), except clamps the value to uint8_t range.
+ */
 void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj,
                       Error **errp);
+
+/*
+ * Visit a uint16_t value.
+ * Like visit_type_int(), except clamps the value to uint16_t range.
+ */
 void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj,
                        Error **errp);
+
+/*
+ * Visit a uint32_t value.
+ * Like visit_type_int(), except clamps the value to uint32_t range.
+ */
 void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj,
                        Error **errp);
+
+/*
+ * Visit a uint64_t value.
+ * Like visit_type_int(), except clamps the value to uint64_t range,
+ * that is, ensures it is unsigned.
+ */
 void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj,
                        Error **errp);
+
+/*
+ * Visit an int8_t value.
+ * Like visit_type_int(), except clamps the value to int8_t range.
+ */
 void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp);
+
+/*
+ * Visit an int16_t value.
+ * Like visit_type_int(), except clamps the value to int16_t range.
+ */
 void visit_type_int16(Visitor *v, const char *name, int16_t *obj,
                       Error **errp);
+
+/*
+ * Visit an int32_t value.
+ * Like visit_type_int(), except clamps the value to int32_t range.
+ */
 void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
                       Error **errp);
+
+/*
+ * Visit an int64_t value.
+ * Identical to visit_type_int().
+ */
 void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
                       Error **errp);
+
+/*
+ * Visit a uint64_t value.
+ * Like visit_type_uint64(), except that some visitors may choose to
+ * recognize additional syntax, such as suffixes for easily scaling
+ * values.
+ */
 void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
                      Error **errp);
+
+/*
+ * Visit a boolean value.
+ *
+ * @name expresses the relationship of this boolean to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL.  Input visitors set *@obj to the value;
+ * other visitors will leave *@obj unchanged.
+ */
 void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
+
+/*
+ * Visit a string value.
+ *
+ * @name expresses the relationship of this string to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL.  Input visitors set *@obj to the value
+ * (never NULL).  Other visitors leave *@obj unchanged, and commonly
+ * treat NULL like "".
+ *
+ * It is safe to cast away const when preparing a (const char *) value
+ * into @obj for use by an output visitor.
+ *
+ * FIXME: Callers that try to output NULL *obj should not be allowed.
+ */
 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
+
+/*
+ * Visit a number (i.e. double) value.
+ *
+ * @name expresses the relationship of this number to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL.  Input visitors set *@obj to the value;
+ * other visitors will leave *@obj unchanged.  Visitors should
+ * document if infinity or NaN are not permitted.
+ */
 void visit_type_number(Visitor *v, const char *name, double *obj,
                        Error **errp);
+
+/*
+ * Visit an arbitrary value.
+ *
+ * @name expresses the relationship of this value to its parent
+ * container; see the general description of @name above.
+ *
+ * @obj must be non-NULL.  Input visitors set *@obj to the value;
+ * other visitors will leave *@obj unchanged.  *@obj must be non-NULL
+ * for output visitors.
+ */
 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);

 #endif
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 51c338a..796d180 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -15,6 +15,18 @@
 #include "qapi/visitor.h"

 /*
+ * This file describes the callback interface for implementing a QAPI
+ * 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.  A callback's contract
+ * matches the corresponding public functions' contract unless stated
+ * otherwise.  In the comments below, some callbacks are marked "must
+ * be set for $TYPE visits to work"; if a visitor implementation omits
+ * that callback, it should also document that it is only useful for a
+ * subset of QAPI.
+ */
+
+/*
  * There are three classes of visitors; setting the class determines
  * how QAPI enums are visited, as well as what additional restrictions
  * can be asserted.
@@ -27,43 +39,59 @@ typedef enum VisitorType {

 struct Visitor
 {
-    /* Must be set */
+    /* Must be set to visit structs */
     void (*start_struct)(Visitor *v, const char *name, void **obj,
                          size_t size, Error **errp);
+
+    /* Must be set to visit structs */
     void (*end_struct)(Visitor *v, Error **errp);

+    /* Must be set */
     void (*start_list)(Visitor *v, const char *name, Error **errp);
+
     /* Must be set */
     GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size);
+
     /* Must be set */
     void (*end_list)(Visitor *v);

-    /* Optional, needed for input and dealloc visitors.  */
+    /* Must be set by input and dealloc visitors to visit alternates;
+     * optional for output visitors. */
     void (*start_alternate)(Visitor *v, const char *name,
                             GenericAlternate **obj, size_t size,
                             bool promote_int, Error **errp);

-    /* Optional, needed for dealloc visitor.  */
+    /* Optional, needed for dealloc visitor */
     void (*end_alternate)(Visitor *v);

-    /* Must be set. */
+    /* Must be set */
     void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
                        Error **errp);
-    /* Must be set. */
+
+    /* Must be set */
     void (*type_uint64)(Visitor *v, const char *name, uint64_t *obj,
                         Error **errp);
-    /* Optional; fallback is type_uint64().  */
+
+    /* Optional; fallback is type_uint64() */
     void (*type_size)(Visitor *v, const char *name, uint64_t *obj,
                       Error **errp);
-    /* Must be set. */
+
+    /* Must be set */
     void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp);
+
+    /* Must be set */
     void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp);
+
+    /* Must be set to visit numbers */
     void (*type_number)(Visitor *v, const char *name, double *obj,
                         Error **errp);
+
+    /* Must be set to visit arbitrary QTypes */
     void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);

-    /* May be NULL; most useful for input visitors. */
+    /* Must be set for input visitors, optional otherwise.  The core
+     * takes care of the return type in the public interface. */
     void (*optional)(Visitor *v, const char *name, bool *present);

     /* Must be set */
diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h
index cf4c36d..45b06b2 100644
--- a/include/qapi/dealloc-visitor.h
+++ b/include/qapi/dealloc-visitor.h
@@ -18,6 +18,11 @@

 typedef struct QapiDeallocVisitor QapiDeallocVisitor;

+/*
+ * The dealloc visitor is primarly used only by generated
+ * qapi_free_FOO() functions, and is the only visitor designed to work
+ * correctly in the face of a partially-constructed QAPI tree.
+ */
 QapiDeallocVisitor *qapi_dealloc_visitor_new(void);
 void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d);

diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h
index fd48c14..633aa71 100644
--- a/include/qapi/opts-visitor.h
+++ b/include/qapi/opts-visitor.h
@@ -29,6 +29,9 @@ typedef struct OptsVisitor OptsVisitor;
  * - string representations of negative numbers yield negative values,
  * - values below INT64_MIN or LLONG_MIN are rejected,
  * - values above INT64_MAX or LLONG_MAX are rejected.
+ *
+ * The Opts input visitor does not implement support for visiting QAPI
+ * alternates, numbers (other than integers), or arbitrary QTypes.
  */
 OptsVisitor *opts_visitor_new(const QemuOpts *opts);
 void opts_visitor_cleanup(OptsVisitor *nv);
diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h
index 089243c..fdf33ae 100644
--- a/include/qapi/string-input-visitor.h
+++ b/include/qapi/string-input-visitor.h
@@ -17,6 +17,10 @@

 typedef struct StringInputVisitor StringInputVisitor;

+/*
+ * The string input visitor does not implement support for visiting
+ * QAPI structs, alternates, or arbitrary QTypes.
+ */
 StringInputVisitor *string_input_visitor_new(const char *str);
 void string_input_visitor_cleanup(StringInputVisitor *v);

diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
index d99717f..3bb09af 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -17,6 +17,10 @@

 typedef struct StringOutputVisitor StringOutputVisitor;

+/*
+ * The string output visitor does not implement support for visiting
+ * QAPI structs, alternates, or arbitrary QTypes.
+ */
 StringOutputVisitor *string_output_visitor_new(bool human);
 void string_output_visitor_cleanup(StringOutputVisitor *v);

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 7ad5ff4..0b7c04a 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -25,6 +25,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
 {
     Error *err = NULL;

+    if (obj) {
+        assert(size);
+        assert(v->type != VISITOR_OUTPUT || *obj);
+    }
     v->start_struct(v, name, obj, size, &err);
     if (obj && v->type == VISITOR_INPUT) {
         assert(!err != !*obj);
@@ -60,6 +64,7 @@ void visit_start_alternate(Visitor *v, const char *name,
     Error *err = NULL;

     assert(obj && size >= sizeof(GenericAlternate));
+    assert(v->type != VISITOR_OUTPUT || *obj);
     if (v->start_alternate) {
         v->start_alternate(v, name, obj, size, promote_int, &err);
     }
@@ -86,6 +91,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present)

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

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

@@ -180,12 +187,14 @@ void visit_type_int32(Visitor *v, const char *name, int32_t *obj,
 void visit_type_int64(Visitor *v, const char *name, int64_t *obj,
                       Error **errp)
 {
+    assert(obj);
     v->type_int64(v, name, obj, errp);
 }

 void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
                      Error **errp)
 {
+    assert(obj);
     if (v->type_size) {
         v->type_size(v, name, obj, errp);
     } else {
@@ -195,6 +204,7 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj,

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

@@ -203,6 +213,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
     Error *err = NULL;

     assert(obj);
+    /* TODO: Fix callers to not pass NULL when they mean "", so that we
+     * can enable:
+    assert(v->type != VISITOR_OUTPUT || *obj);
+     */
     v->type_str(v, name, obj, &err);
     if (v->type == VISITOR_INPUT) {
         assert(!err != !*obj);
@@ -213,6 +227,7 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 void visit_type_number(Visitor *v, const char *name, double *obj,
                        Error **errp)
 {
+    assert(obj);
     v->type_number(v, name, obj, errp);
 }

@@ -221,6 +236,7 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
     Error *err = NULL;

     assert(obj);
+    assert(v->type != VISITOR_OUTPUT || *obj);
     v->type_any(v, name, obj, &err);
     if (v->type == VISITOR_INPUT) {
         assert(!err != !*obj);
@@ -278,7 +294,7 @@ static void input_type_enum(Visitor *v, const char *name, int *obj,
 void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp)
 {
-    assert(strings);
+    assert(obj && strings);
     if (v->type == VISITOR_INPUT) {
         input_type_enum(v, name, obj, strings, errp);
     } else if (v->type == VISITOR_OUTPUT) {
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 13/24] tests: Add check-qnull
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (11 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 12/24] qapi: Document visitor interfaces, add assertions Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 14/24] qapi: Add visit_type_null() visitor Eric Blake
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Add a new test, for checking reference counting of qnull(). As
part of the new file, move a previous reference counting change
added in commit a861564 to a more logical place.

Note that while most of the check-q*.c leave visitor stuff to
the test-qmp-*-visitor.c, in this case we actually want the
visitor tests in our new file because we are validating the
reference count of qnull_, which is an internal detail that
test-qmp-*-visitor should not be peeking into (or put another
way, qnull() is the only special case where we don't have
independent allocation of a QObject, so none of the other
visitor tests require the layering violation present in this
test).

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

---
v15: add comment, commit message tweak
v14: no change
v13: no change
v12: new patch
---
 tests/check-qnull.c             | 66 +++++++++++++++++++++++++++++++++++++++++
 tests/test-qmp-output-visitor.c |  2 --
 tests/.gitignore                |  1 +
 tests/Makefile                  |  6 +++-
 4 files changed, 72 insertions(+), 3 deletions(-)
 create mode 100644 tests/check-qnull.c

diff --git a/tests/check-qnull.c b/tests/check-qnull.c
new file mode 100644
index 0000000..4a1c3d8
--- /dev/null
+++ b/tests/check-qnull.c
@@ -0,0 +1,66 @@
+/*
+ * QNull unit-tests.
+ *
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include <glib.h>
+
+#include "qapi/qmp/qobject.h"
+#include "qemu-common.h"
+#include "qapi/qmp-output-visitor.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+static void qnull_ref_test(void)
+{
+    QObject *obj;
+
+    g_assert(qnull_.refcnt == 1);
+    obj = qnull();
+    g_assert(obj);
+    g_assert(obj == &qnull_);
+    g_assert(qnull_.refcnt == 2);
+    g_assert(qobject_type(obj) == QTYPE_QNULL);
+    qobject_decref(obj);
+    g_assert(qnull_.refcnt == 1);
+}
+
+static void qnull_visit_test(void)
+{
+    QObject *obj;
+    QmpOutputVisitor *qov;
+
+    /*
+     * Most tests of interactions between QObject and visitors are in
+     * test-qmp-*-visitor; but these tests live here because they
+     * depend on layering violations to check qnull_ refcnt.
+     */
+
+    g_assert(qnull_.refcnt == 1);
+    qov = qmp_output_visitor_new();
+    /* FIXME: Empty visits are ugly, we should have a visit_type_null(). */
+    obj = qmp_output_get_qobject(qov);
+    g_assert(obj == &qnull_);
+    qobject_decref(obj);
+
+    qmp_output_visitor_cleanup(qov);
+    g_assert(qnull_.refcnt == 1);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/public/qnull_ref", qnull_ref_test);
+    g_test_add_func("/public/qnull_visit", qnull_visit_test);
+
+    return g_test_run();
+}
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index c709267..fddb5a6 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -484,8 +484,6 @@ 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);
 }

diff --git a/tests/.gitignore b/tests/.gitignore
index 9eed229..a06a8ba 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -3,6 +3,7 @@ check-qfloat
 check-qint
 check-qjson
 check-qlist
+check-qnull
 check-qstring
 check-qom-interface
 check-qom-proplist
diff --git a/tests/Makefile b/tests/Makefile
index 9194f18..9dddde6 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -16,6 +16,8 @@ check-unit-y += tests/check-qstring$(EXESUF)
 gcov-files-check-qstring-y = qobject/qstring.c
 check-unit-y += tests/check-qlist$(EXESUF)
 gcov-files-check-qlist-y = qobject/qlist.c
+check-unit-y += tests/check-qnull$(EXESUF)
+gcov-files-check-qnull-y = qobject/qnull.c
 check-unit-y += tests/check-qjson$(EXESUF)
 gcov-files-check-qjson-y = qobject/qjson.c
 check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
@@ -382,7 +384,8 @@ GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
 	tests/test-qmp-introspect.h

 test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
-	tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \
+	tests/check-qlist.o tests/check-qfloat.o tests/check-qnull.o \
+	tests/check-qjson.o \
 	tests/test-coroutine.o tests/test-string-output-visitor.o \
 	tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \
 	tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
@@ -410,6 +413,7 @@ tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
 tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
 tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
 tests/check-qfloat$(EXESUF): tests/check-qfloat.o $(test-util-obj-y)
+tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y)
 tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
 tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
 tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 14/24] qapi: Add visit_type_null() visitor
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (12 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 13/24] tests: Add check-qnull Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 15/24] qmp: Support explicit null during visits Eric Blake
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 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.  Yes, we could say
that such a visit must go through visit_type_any(), but that
feels clunky.

So this patch introduces the new visit_type_null() interface and
its no-op interface in the dealloc visitor, and stubs in the
qmp visitors (the next patch will finish the implementation).
For the visitors that will not implement the callback, document
the situation. The code in qapi-visit-core unconditionally
dereferences the callback pointer, so that a segfault will inform
a developer if they need to implement the callback for their
choice of visitor.

Note that JSON has a primitive null type, with the single value
null; likewise with the QNull type for QObject; but for QAPI,
we just have the 'null' value without a null type.  We may
eventually want to add more support in QAPI for null (most likely,
we'd use it via an alternate type that permits 'null' or an
object); but we'll create that usage when we need it.

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

---
v16: make stubs simply abort()
v15: improve commit message, add stubs here
v14: no change
v13: no change
v12: rebase to earlier changes, drop R-b due to better documentation
[no v10, v11]
v9: no change
v8: rebase to 'name' motion
v7: new patch, based on discussion about spapr_drc.c
---
 include/qapi/visitor.h               | 12 ++++++++++++
 include/qapi/visitor-impl.h          |  3 +++
 include/qapi/opts-visitor.h          |  3 ++-
 include/qapi/string-input-visitor.h  |  2 +-
 include/qapi/string-output-visitor.h |  2 +-
 qapi/qapi-visit-core.c               |  5 +++++
 qapi/qapi-dealloc-visitor.c          |  5 +++++
 qapi/qmp-input-visitor.c             |  6 ++++++
 qapi/qmp-output-visitor.c            |  6 ++++++
 9 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 0bb2fe1..841a1bf 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -521,4 +521,16 @@ void visit_type_number(Visitor *v, const char *name, double *obj,
  */
 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);

+/*
+ * Visit a JSON null value.
+ *
+ * @name expresses the relationship of the null value to its parent
+ * container; see the general description of @name above.
+ *
+ * Unlike all other visit_type_* functions, 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);
+
 #endif
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 796d180..88d27d5 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -90,6 +90,9 @@ struct Visitor
     void (*type_any)(Visitor *v, const char *name, QObject **obj,
                      Error **errp);

+    /* Must be set to visit explicit null values.  */
+    void (*type_null)(Visitor *v, const char *name, Error **errp);
+
     /* Must be set for input visitors, optional otherwise.  The core
      * takes care of the return type in the public interface. */
     void (*optional)(Visitor *v, const char *name, bool *present);
diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h
index 633aa71..fe37ed9 100644
--- a/include/qapi/opts-visitor.h
+++ b/include/qapi/opts-visitor.h
@@ -31,7 +31,8 @@ typedef struct OptsVisitor OptsVisitor;
  * - values above INT64_MAX or LLONG_MAX are rejected.
  *
  * The Opts input visitor does not implement support for visiting QAPI
- * alternates, numbers (other than integers), or arbitrary QTypes.
+ * alternates, numbers (other than integers), null, or arbitrary
+ * QTypes.
  */
 OptsVisitor *opts_visitor_new(const QemuOpts *opts);
 void opts_visitor_cleanup(OptsVisitor *nv);
diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h
index fdf33ae..a8d8f67 100644
--- a/include/qapi/string-input-visitor.h
+++ b/include/qapi/string-input-visitor.h
@@ -19,7 +19,7 @@ typedef struct StringInputVisitor StringInputVisitor;

 /*
  * The string input visitor does not implement support for visiting
- * QAPI structs, alternates, or arbitrary QTypes.
+ * QAPI structs, alternates, null, or arbitrary QTypes.
  */
 StringInputVisitor *string_input_visitor_new(const char *str);
 void string_input_visitor_cleanup(StringInputVisitor *v);
diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
index 3bb09af..89b7e4b 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -19,7 +19,7 @@ typedef struct StringOutputVisitor StringOutputVisitor;

 /*
  * The string output visitor does not implement support for visiting
- * QAPI structs, alternates, or arbitrary QTypes.
+ * QAPI structs, alternates, null, or arbitrary QTypes.
  */
 StringOutputVisitor *string_output_visitor_new(bool human);
 void string_output_visitor_cleanup(StringOutputVisitor *v);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 0b7c04a..7ca8bf0 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -244,6 +244,11 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
     error_propagate(errp, err);
 }

+void visit_type_null(Visitor *v, const char *name, Error **errp)
+{
+    v->type_null(v, name, errp);
+}
+
 static void output_type_enum(Visitor *v, const char *name, int *obj,
                              const char *const strings[], Error **errp)
 {
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index c19a459..413d525 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -163,6 +163,10 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name,
     }
 }

+static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
+{
+}
+
 Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
 {
     return &v->visitor;
@@ -193,6 +197,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;

     QTAILQ_INIT(&v->stack);

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 5159833..fa460a3 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -340,6 +340,11 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
     *obj = qobj;
 }

+static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
+{
+    abort();
+}
+
 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -383,6 +388,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
     v->visitor.type_any = qmp_input_type_any;
+    v->visitor.type_null = qmp_input_type_null;
     v->visitor.optional = qmp_input_optional;
     v->strict = strict;

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 1f2a7ba..adf7731 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -196,6 +196,11 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,
     qmp_output_add_obj(qov, name, *obj);
 }

+static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
+{
+    abort();
+}
+
 /* Finish building, and return the root object. Will not be NULL. */
 QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 {
@@ -246,6 +251,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);

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 15/24] qmp: Support explicit null during visits
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (13 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 14/24] qapi: Add visit_type_null() visitor Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 16/24] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Implement the new type_null() callback for the qmp input and
output visitors. While we don't yet have a use for this in QAPI
input (the generator will need some tweaks first), some
potential usages have already been discussed on the list.
Meanwhile, the output visitor could already output explicit null
via type_any, but this gives us finer control.

At any rate, it's easy to test that we can round-trip an explicit
null through manual use of visit_type_null() wrapped by a virtual
visit_start_struct() walk, even if we can't do the visit in a
QAPI type.  Repurpose the test_visitor_out_empty test,
particularly since a future patch will tighten semantics to
forbid use of qmp_output_get_qobject() without at least one
intervening visit_type_*.

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

---
v16: one more testsuite enhancement
v15: hoist stubs to earlier patch, testsuite improvements
v14: rebase to header inclusion cleanups
v13: no change
v12: retitle and merge in output visitor support, move refcount
tests to separate file
[no v10, v11]
v9: new patch
---
 qapi/qmp-input-visitor.c        |  8 +++++++-
 qapi/qmp-output-visitor.c       |  3 ++-
 tests/check-qnull.c             | 13 +++++++++++--
 tests/test-qmp-input-visitor.c  | 29 +++++++++++++++++++++++++++++
 tests/test-qmp-output-visitor.c | 20 +++++++++++++++-----
 5 files changed, 64 insertions(+), 9 deletions(-)

diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index fa460a3..30e7cb3 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -342,7 +342,13 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,

 static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
 {
-    abort();
+    QmpInputVisitor *qiv = to_qiv(v);
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+
+    if (qobject_type(qobj) != QTYPE_QNULL) {
+        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                   "null");
+    }
 }

 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index adf7731..5681ad3 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -198,7 +198,8 @@ static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj,

 static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
 {
-    abort();
+    QmpOutputVisitor *qov = to_qov(v);
+    qmp_output_add_obj(qov, name, qnull());
 }

 /* Finish building, and return the root object. Will not be NULL. */
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index 4a1c3d8..fd9c68f 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -11,7 +11,9 @@

 #include "qapi/qmp/qobject.h"
 #include "qemu-common.h"
+#include "qapi/qmp-input-visitor.h"
 #include "qapi/qmp-output-visitor.h"
+#include "qapi/error.h"

 /*
  * Public Interface test-cases
@@ -37,6 +39,7 @@ static void qnull_visit_test(void)
 {
     QObject *obj;
     QmpOutputVisitor *qov;
+    QmpInputVisitor *qiv;

     /*
      * Most tests of interactions between QObject and visitors are in
@@ -45,13 +48,19 @@ static void qnull_visit_test(void)
      */

     g_assert(qnull_.refcnt == 1);
+    obj = qnull();
+    qiv = qmp_input_visitor_new(obj, true);
+    qobject_decref(obj);
+    visit_type_null(qmp_input_get_visitor(qiv), NULL, &error_abort);
+    qmp_input_visitor_cleanup(qiv);
+
     qov = qmp_output_visitor_new();
-    /* FIXME: Empty visits are ugly, we should have a visit_type_null(). */
+    visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort);
     obj = qmp_output_get_qobject(qov);
     g_assert(obj == &qnull_);
     qobject_decref(obj);
-
     qmp_output_visitor_cleanup(qov);
+
     g_assert(qnull_.refcnt == 1);
 }

diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index c039806..10e00ec 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -279,6 +279,33 @@ static void test_visitor_in_any(TestInputVisitorData *data,
     qobject_decref(res);
 }

+static void test_visitor_in_null(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    Visitor *v;
+    Error *err = NULL;
+    char *tmp;
+
+    /*
+     * FIXME: Since QAPI doesn't know the 'null' type yet, we can't
+     * test visit_type_null() by reading into a QAPI struct then
+     * checking that it was populated correctly.  The best we can do
+     * for now is ensure that we consumed null from the input, proven
+     * by the fact that we can't re-read the key; and that we detect
+     * when input is not null.
+     */
+
+    v = visitor_input_test_init(data, "{ 'a': null, 'b': '' }");
+    visit_start_struct(v, NULL, NULL, 0, &error_abort);
+    visit_type_null(v, "a", &error_abort);
+    visit_type_str(v, "a", &tmp, &err);
+    g_assert(!tmp);
+    error_free_or_abort(&err);
+    visit_type_null(v, "b", &err);
+    error_free_or_abort(&err);
+    visit_end_struct(v, &error_abort);
+}
+
 static void test_visitor_in_union_flat(TestInputVisitorData *data,
                                        const void *unused)
 {
@@ -829,6 +856,8 @@ int main(int argc, char **argv)
                            &in_visitor_data, test_visitor_in_list);
     input_visitor_test_add("/visitor/input/any",
                            &in_visitor_data, test_visitor_in_any);
+    input_visitor_test_add("/visitor/input/null",
+                           &in_visitor_data, test_visitor_in_null);
     input_visitor_test_add("/visitor/input/union-flat",
                            &in_visitor_data, test_visitor_in_union_flat);
     input_visitor_test_add("/visitor/input/alternate",
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index fddb5a6..8acc229 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -477,13 +477,23 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     qobject_decref(arg);
 }

-static void test_visitor_out_empty(TestOutputVisitorData *data,
-                                   const void *unused)
+static void test_visitor_out_null(TestOutputVisitorData *data,
+                                  const void *unused)
 {
     QObject *arg;
+    QDict *qdict;
+    QObject *nil;

+    visit_start_struct(data->ov, NULL, NULL, 0, &error_abort);
+    visit_type_null(data->ov, "a", &error_abort);
+    visit_end_struct(data->ov, &error_abort);
     arg = qmp_output_get_qobject(data->qov);
-    g_assert(qobject_type(arg) == QTYPE_QNULL);
+    g_assert(qobject_type(arg) == QTYPE_QDICT);
+    qdict = qobject_to_qdict(arg);
+    g_assert_cmpint(qdict_size(qdict), ==, 1);
+    nil = qdict_get(qdict, "a");
+    g_assert(nil);
+    g_assert(qobject_type(nil) == QTYPE_QNULL);
     qobject_decref(arg);
 }

@@ -837,8 +847,8 @@ int main(int argc, char **argv)
                             &out_visitor_data, test_visitor_out_union_flat);
     output_visitor_test_add("/visitor/output/alternate",
                             &out_visitor_data, test_visitor_out_alternate);
-    output_visitor_test_add("/visitor/output/empty",
-                            &out_visitor_data, test_visitor_out_empty);
+    output_visitor_test_add("/visitor/output/null",
+                            &out_visitor_data, test_visitor_out_null);
     output_visitor_test_add("/visitor/output/native_list/int",
                             &out_visitor_data,
                             test_visitor_out_native_list_int);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 16/24] spapr_drc: Expose 'null' in qom-get when there is no fdt
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (14 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 15/24] qmp: Support explicit null during visits Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 17/24] qmp: Add qmp_output_visitor_reset() Eric Blake
                   ` (8 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, David Gibson, Alexander Graf, open list:sPAPR

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 ('null') vs. a
present-but-empty one ('{}').

(Note that this reverts the behavior of commit ab8bf1d, taking
us back to the behavior of commit 6c2f9a1 [which in turn
stemmed from a crash fix in 1d10b44]; but that this time,
the change is intentional and not an accidental side-effect.)

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

---
v15: no change
v14: no change
v13: no change
v12: tweak commit message
[no v10, v11]
v9: improved commit message
v8: rebase to 'name' motion
v7: new patch, based on discussion about spapr_drc.c
---
 hw/ppc/spapr_drc.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 1f5f1d7..480a8c6 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -269,11 +269,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
     void *fdt;

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

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 17/24] qmp: Add qmp_output_visitor_reset()
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (15 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 16/24] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-05-10  4:20   ` [Qemu-devel] [PATCH v16A 17/24] qmp: Don't reuse qmp visitor after grabbing output Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 18/24] qmp: Tighten output visitor rules Eric Blake
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Add a new qmp_output_visitor_reset(), to make it easier for a
caller to reset all state while still reusing an existing
visitor, regardless of whether the previous visit was
successfully completed.  Then use it in the testsuite.

The tests needing patching were found by tightening asserts
in the QMP output visitor; see the next patch.  An audit of
all other users of qmp_output_visitor_new() did not find any
other attempts to reuse a visitor.

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

---
v15: new patch, split off of v14 13/19
---
 include/qapi/qmp-output-visitor.h | 1 +
 qapi/qmp-output-visitor.c         | 8 +++++++-
 tests/test-qmp-output-visitor.c   | 6 ++++++
 3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
index 2266770..5093f0d 100644
--- a/include/qapi/qmp-output-visitor.h
+++ b/include/qapi/qmp-output-visitor.h
@@ -21,6 +21,7 @@ typedef struct QmpOutputVisitor QmpOutputVisitor;

 QmpOutputVisitor *qmp_output_visitor_new(void);
 void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
+void qmp_output_visitor_reset(QmpOutputVisitor *v);

 QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
 Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 5681ad3..6c44210 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -221,7 +221,7 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
     return &v->visitor;
 }

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

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

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

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 8acc229..0e83099 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -139,6 +139,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
         g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
                         EnumOne_lookup[i]);
         qobject_decref(obj);
+        qmp_output_visitor_reset(data->qov);
     }
 }

@@ -153,6 +154,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
         visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
         g_assert(err);
         error_free(err);
+        qmp_output_visitor_reset(data->qov);
     }
 }

@@ -262,6 +264,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
         visit_type_UserDefOne(data->ov, "unused", &pu, &err);
         g_assert(err);
         error_free(err);
+        qmp_output_visitor_reset(data->qov);
     }
 }

@@ -366,6 +369,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qobject_decref(obj);
     qobject_decref(qobj);

+    qmp_output_visitor_reset(data->qov);
     qdict = qdict_new();
     qdict_put(qdict, "integer", qint_from_int(-42));
     qdict_put(qdict, "boolean", qbool_from_bool(true));
@@ -442,6 +446,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     qapi_free_UserDefAlternate(tmp);
     qobject_decref(arg);

+    qmp_output_visitor_reset(data->qov);
     tmp = g_new0(UserDefAlternate, 1);
     tmp->type = QTYPE_QSTRING;
     tmp->u.s = g_strdup("hello");
@@ -455,6 +460,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     qapi_free_UserDefAlternate(tmp);
     qobject_decref(arg);

+    qmp_output_visitor_reset(data->qov);
     tmp = g_new0(UserDefAlternate, 1);
     tmp->type = QTYPE_QDICT;
     tmp->u.udfu.integer = 1;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 18/24] qmp: Tighten output visitor rules
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (16 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 17/24] qmp: Add qmp_output_visitor_reset() Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 19/24] qapi: Split visit_end_struct() into pieces Eric Blake
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Tighten assertions in the QMP output visitor, so that:

- qmp_output_get_qobject() can only be called after pairing a
visit_end_* for every visit_start_* (rather than allowing it on
a partially built object)

- qmp_output_get_qobject() cannot be called unless at least one
visit_type_* or visit_start/visit_end pair has occurred since
creation/reset (the accidental return of NULL fixed by commit
ab8bf1d7 would have been much easier to diagnose)

- ensure that we are encountering the expected object or list
type, to provide protection against mismatched push(struct)/
pop(list) or push(list)/pop(struct), similar to the qmp-input
protection added in commit bdd8e6b5.

- ensure that except for the root, 'name' is non-null inside a
dict, and NULL inside a list (this may need changing later if
we add "name.0" support for better error messages for a list,
but for now it makes sure all users are at least consistent)

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

---
v15: split off qmp_output_visitor_reset(), improve comments,
use QTAILQ_EMPTY
v14: no change
v13: no change
v12: rebase to latest, move type_null() into earlier patches,
don't change signature of pop, don't auto-reset after a single
get_qobject
[no v10, v11]
v9: rebase to added patch, squash in more sanity checks, drop
Marc-Andre's R-b
v8: rename qmp_output_reset to qmp_output_visitor_reset
v7: new patch, based on discussion about spapr_drc.c

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 qapi/qmp-output-visitor.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 6c44210..7155bde 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -82,9 +82,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
     QObject *cur = e ? e->value : NULL;

     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)) {
@@ -93,6 +92,7 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
             qdict_put_obj(qobject_to_qdict(cur), name, value);
             break;
         case QTYPE_QLIST:
+            assert(!name);
             qlist_append_obj(qobject_to_qlist(cur), value);
             break;
         default:
@@ -114,7 +114,8 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
 static void qmp_output_end_struct(Visitor *v, Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    qmp_output_pop(qov);
+    QObject *value = qmp_output_pop(qov);
+    assert(qobject_type(value) == QTYPE_QDICT);
 }

 static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
@@ -145,7 +146,8 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
 static void qmp_output_end_list(Visitor *v)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    qmp_output_pop(qov);
+    QObject *value = qmp_output_pop(qov);
+    assert(qobject_type(value) == QTYPE_QLIST);
 }

 static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj,
@@ -202,18 +204,16 @@ static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
     qmp_output_add_obj(qov, name, qnull());
 }

-/* Finish building, and return the root object. Will not be NULL. */
+/* Finish building, and return the root object.
+ * The root object is never null. The caller becomes the object's
+ * owner, and should use qobject_decref() when done with it.  */
 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;
+    /* A visit must have occurred, with each start paired with end.  */
+    assert(qov->root && QTAILQ_EMPTY(&qov->stack));
+
+    qobject_incref(qov->root);
+    return qov->root;
 }

 Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 19/24] qapi: Split visit_end_struct() into pieces
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (17 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 18/24] qmp: Tighten output visitor rules Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 20/24] qapi: Don't pass NULL to printf in string input visitor Eric Blake
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Michael Roth, David Gibson,
	Alexander Graf, Michael S. Tsirkin, Andreas Färber,
	open list:Block layer core, open list:sPAPR

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.  So, split out the error checking
portion (basically, input visitors checking for unvisited keys) into
a new function visit_check_struct(), which can be safely skipped if
any earlier errors are encountered, and leave the cleanup portion
(which never fails, but must be called unconditionally if
visit_start_struct() succeeded) in visit_end_struct().

Generated code in qapi-visit.c has diffs resembling:

|@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v,
|         goto out_obj;
|     }
|     visit_type_ACPIOSTInfo_members(v, obj, &err);
|-    error_propagate(errp, err);
|-    err = NULL;
|+    if (err) {
|+        goto out_obj;
|+    }
|+    visit_check_struct(v, &err);
| out_obj:
|-    visit_end_struct(v, &err);
|+    visit_end_struct(v);
| out:

and in qapi-event.c:

@@ -47,7 +47,10 @@ void qapi_event_send_acpi_device_ost(ACP
|         goto out;
|     }
|     visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, &param, &err);
|-    visit_end_struct(v, err ? NULL : &err);
|+    if (!err) {
|+        visit_check_struct(v, &err);
|+    }
|+    visit_end_struct(v);
|     if (err) {
|         goto out;

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

---
v16: rebase to earlier changes
v15: rebase, simplify user_creatable_add
v14: rebase to master
v13: rebase to earlier changes
v12: rebase to earlier changes, fix bug in spapr_drc not calling
visit_end_struct, fold in docs, fix stray DO_UPCAST from sneaking
back in
[no v10, v11]
v9: rebase to earlier changes, drop Marc-Andre's R-b
v8: rebase to 'name' motion
v7: rebase to earlier changes
v6: new patch, revised version of RFC based on discussion of v5 7/46
---
 include/qapi/visitor.h          | 21 ++++++++++++++++-----
 include/qapi/visitor-impl.h     |  5 ++++-
 scripts/qapi-commands.py        |  7 +++++--
 scripts/qapi-event.py           |  5 ++++-
 scripts/qapi-visit.py           | 15 +++++++++------
 qapi/qapi-visit-core.c          | 11 +++++++++--
 block/crypto.c                  | 14 ++++++++------
 hw/ppc/spapr_drc.c              |  3 ++-
 hw/virtio/virtio-balloon.c      | 15 ++++++++-------
 qapi/opts-visitor.c             | 17 +++++++++++++++--
 qapi/qapi-dealloc-visitor.c     |  2 +-
 qapi/qmp-input-visitor.c        | 35 ++++++++++++++++++++---------------
 qapi/qmp-output-visitor.c       |  2 +-
 qom/object.c                    |  5 ++---
 qom/object_interfaces.c         | 25 +++++++++++--------------
 tests/test-qmp-input-visitor.c  |  3 ++-
 tests/test-qmp-output-visitor.c |  3 ++-
 docs/qapi-code-gen.txt          | 15 ++++++++++-----
 18 files changed, 129 insertions(+), 74 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 841a1bf..a7a0cb8 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -192,10 +192,11 @@
  *  }
  * outlist:
  *  visit_end_list(v);
+ *  if (!err) {
+ *      visit_check_struct(v, &err);
+ *  }
  * outobj:
- *  error_propagate(errp, err);
- *  err = NULL;
- *  visit_end_struct(v, &err);
+ *  visit_end_struct(v);
  *  error_propagate(errp, err);
  *  ...clean up v...
  * </example>
@@ -248,17 +249,27 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp);

 /*
- * Complete an object visit started earlier.
+ * Prepare for completing an object visit.
  *
  * @errp obeys typical error usage, and reports failures such as
  * unparsed keys remaining in the input stream.
  *
+ * Should be called prior to visit_end_struct() if all other
+ * intermediate visit steps were successful, to allow the visitor one
+ * last chance to report errors.  May be skipped on a cleanup path,
+ * where there is no need to check for further errors.
+ */
+void visit_check_struct(Visitor *v, Error **errp);
+
+/*
+ * Complete an object 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.  Destroying the visitor early
  * behaves as if this was implicitly called.
  */
-void visit_end_struct(Visitor *v, Error **errp);
+void visit_end_struct(Visitor *v);


 /*** Visiting lists ***/
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 88d27d5..b20a922 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -43,8 +43,11 @@ struct Visitor
     void (*start_struct)(Visitor *v, const char *name, void **obj,
                          size_t size, Error **errp);

+    /* Optional; intended for input visitors */
+    void (*check_struct)(Visitor *v, Error **errp);
+
     /* Must be set to visit structs */
-    void (*end_struct)(Visitor *v, Error **errp);
+    void (*end_struct)(Visitor *v);

     /* Must be set */
     void (*start_list)(Visitor *v, const char *name, Error **errp);
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 04549fa..8c6acb3 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -126,7 +126,10 @@ def gen_marshal(name, arg_type, ret_type):
         goto out;
     }
     visit_type_%(c_name)s_members(v, &arg, &err);
-    visit_end_struct(v, err ? NULL : &err);
+    if (!err) {
+        visit_check_struct(v, &err);
+    }
+    visit_end_struct(v);
     if (err) {
         goto out;
     }
@@ -157,7 +160,7 @@ out:
     v = qapi_dealloc_get_visitor(qdv);
     visit_start_struct(v, NULL, NULL, 0, NULL);
     visit_type_%(c_name)s_members(v, &arg, NULL);
-    visit_end_struct(v, NULL);
+    visit_end_struct(v);
     qapi_dealloc_visitor_cleanup(qdv);
 ''',
                      c_name=arg_type.c_name())
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 9b5c5b5..21fb167 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -98,7 +98,10 @@ def gen_event_send(name, arg_type):
         goto out;
     }
     visit_type_%(c_name)s_members(v, &param, &err);
-    visit_end_struct(v, err ? NULL : &err);
+    if (!err) {
+        visit_check_struct(v, &err);
+    }
+    visit_end_struct(v);
     if (err) {
         goto out;
     }
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 31d2330..bdf8971 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -186,9 +186,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
             break;
         }
         visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err);
-        error_propagate(errp, err);
-        err = NULL;
-        visit_end_struct(v, &err);
+        if (!err) {
+            visit_check_struct(v, &err);
+        }
+        visit_end_struct(v);
 ''',
                          c_type=var.type.c_name(),
                          c_name=c_name(var.name))
@@ -236,10 +237,12 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         goto out_obj;
     }
     visit_type_%(c_name)s_members(v, *obj, &err);
-    error_propagate(errp, err);
-    err = NULL;
+    if (err) {
+        goto out_obj;
+    }
+    visit_check_struct(v, &err);
 out_obj:
-    visit_end_struct(v, &err);
+    visit_end_struct(v);
 out:
     error_propagate(errp, err);
 }
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 7ca8bf0..9b99c02 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -36,9 +36,16 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
     error_propagate(errp, err);
 }

-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_list(Visitor *v, const char *name, Error **errp)
diff --git a/block/crypto.c b/block/crypto.c
index 1903e84..2424a4c 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -196,7 +196,6 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
     OptsVisitor *ov;
     QCryptoBlockOpenOptions *ret = NULL;
     Error *local_err = NULL;
-    Error *end_err = NULL;

     ret = g_new0(QCryptoBlockOpenOptions, 1);
     ret->format = format;
@@ -219,9 +218,11 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
         error_setg(&local_err, "Unsupported block format %d", format);
         break;
     }
+    if (!local_err) {
+        visit_check_struct(opts_get_visitor(ov), &local_err);
+    }

-    visit_end_struct(opts_get_visitor(ov), &end_err);
-    error_propagate(&local_err, end_err);
+    visit_end_struct(opts_get_visitor(ov));

  out:
     if (local_err) {
@@ -242,7 +243,6 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
     OptsVisitor *ov;
     QCryptoBlockCreateOptions *ret = NULL;
     Error *local_err = NULL;
-    Error *end_err = NULL;

     ret = g_new0(QCryptoBlockCreateOptions, 1);
     ret->format = format;
@@ -265,9 +265,11 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
         error_setg(&local_err, "Unsupported block format %d", format);
         break;
     }
+    if (!local_err) {
+        visit_check_struct(opts_get_visitor(ov), &local_err);
+    }

-    visit_end_struct(opts_get_visitor(ov), &end_err);
-    error_propagate(&local_err, end_err);
+    visit_end_struct(opts_get_visitor(ov));

  out:
     if (local_err) {
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 480a8c6..e3a6191 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -297,7 +297,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
         case FDT_END_NODE:
             /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
             g_assert(fdt_depth > 0);
-            visit_end_struct(v, &err);
+            visit_check_struct(v, &err);
+            visit_end_struct(v);
             if (err) {
                 error_propagate(errp, err);
                 return;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 9dbe681..8c15e09 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -138,17 +138,18 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
     for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
         visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err);
         if (err) {
-            break;
+            goto out_nested;
         }
     }
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, &err);
+    visit_check_struct(v, &err);
+out_nested:
+    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/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 4cb6436..ece0598 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -159,13 +159,13 @@ opts_start_struct(Visitor *v, const char *name, void **obj,


 static void
-opts_end_struct(Visitor *v, Error **errp)
+opts_check_struct(Visitor *v, Error **errp)
 {
     OptsVisitor *ov = to_ov(v);
     GHashTableIter iter;
     GQueue *any;

-    if (--ov->depth > 0) {
+    if (ov->depth > 0) {
         return;
     }

@@ -177,6 +177,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 = to_ov(v);
+
+    if (--ov->depth > 0) {
+        return;
+    }
+
     g_hash_table_destroy(ov->unprocessed_opts);
     ov->unprocessed_opts = NULL;
     if (ov->fake_id_opt) {
@@ -516,6 +528,7 @@ opts_visitor_new(const QemuOpts *opts)
     ov->visitor.type = VISITOR_INPUT;

     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 413d525..9005bad 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -67,7 +67,7 @@ static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
     qapi_dealloc_push(qov, obj);
 }

-static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
+static void qapi_dealloc_end_struct(Visitor *v)
 {
     QapiDeallocVisitor *qov = to_qov(v);
     void **obj = qapi_dealloc_pop(qov);
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 30e7cb3..90b9df1 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -125,9 +125,11 @@ 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);
     StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
+
     assert(qiv->nb_stack > 0);

     if (qiv->strict) {
@@ -140,6 +142,20 @@ 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);
+    StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
+
+    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);
         }
         tos->h = NULL;
@@ -175,12 +191,6 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
     }
 }

-static void qmp_input_end_struct(Visitor *v, Error **errp)
-{
-    QmpInputVisitor *qiv = to_qiv(v);
-
-    qmp_input_pop(qiv, errp);
-}

 static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
 {
@@ -218,12 +228,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_start_alternate(Visitor *v, const char *name,
                                       GenericAlternate **obj, size_t size,
@@ -383,10 +387,11 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)

     v->visitor.type = VISITOR_INPUT;
     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_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.start_alternate = qmp_input_start_alternate;
     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 7155bde..a66d754 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -111,7 +111,7 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj,
     qmp_output_push(qov, dict);
 }

-static void qmp_output_end_struct(Visitor *v, Error **errp)
+static void qmp_output_end_struct(Visitor *v)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QObject *value = qmp_output_pop(qov);
diff --git a/qom/object.c b/qom/object.c
index 8e6e68d..3bc8a00 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -2036,10 +2036,9 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name,
     if (err) {
         goto out_end;
     }
+    visit_check_struct(v, &err);
 out_end:
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_struct(v, errp);
+    visit_end_struct(v);
 out:
     error_propagate(errp, err);

diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index cad5542..51e62e2 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -42,7 +42,7 @@ Object *user_creatable_add(const QDict *qdict,
     char *type = NULL;
     char *id = NULL;
     Object *obj = NULL;
-    Error *local_err = NULL, *end_err = NULL;
+    Error *local_err = NULL;
     QDict *pdict;

     pdict = qdict_clone_shallow(qdict);
@@ -63,21 +63,15 @@ Object *user_creatable_add(const QDict *qdict,
     if (local_err) {
         goto out_visit;
     }
+    visit_check_struct(v, &local_err);
+    if (local_err) {
+        goto out_visit;
+    }

     obj = user_creatable_add_type(type, id, pdict, v, &local_err);
-    if (local_err) {
-        goto out_visit;
-    }

- out_visit:
-    visit_end_struct(v, &end_err);
-    if (end_err) {
-        error_propagate(&local_err, end_err);
-        if (obj) {
-            user_creatable_del(id, NULL);
-        }
-        goto out;
-    }
+out_visit:
+    visit_end_struct(v);

 out:
     QDECREF(pdict);
@@ -130,7 +124,10 @@ Object *user_creatable_add_type(const char *type, const char *id,
             break;
         }
     }
-    visit_end_struct(v, local_err ? NULL : &local_err);
+    if (!local_err) {
+        visit_check_struct(v, &local_err);
+    }
+    visit_end_struct(v);
     if (local_err) {
         goto out;
     }
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 10e00ec..6617276 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -303,7 +303,8 @@ static void test_visitor_in_null(TestInputVisitorData *data,
     error_free_or_abort(&err);
     visit_type_null(v, "b", &err);
     error_free_or_abort(&err);
-    visit_end_struct(v, &error_abort);
+    visit_check_struct(v, &error_abort);
+    visit_end_struct(v);
 }

 static void test_visitor_in_union_flat(TestInputVisitorData *data,
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 0e83099..0706fe9 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -492,7 +492,8 @@ static void test_visitor_out_null(TestOutputVisitorData *data,

     visit_start_struct(data->ov, NULL, NULL, 0, &error_abort);
     visit_type_null(data->ov, "a", &error_abort);
-    visit_end_struct(data->ov, &error_abort);
+    visit_check_struct(data->ov, &error_abort);
+    visit_end_struct(data->ov);
     arg = qmp_output_get_qobject(data->qov);
     g_assert(qobject_type(arg) == QTYPE_QDICT);
     qdict = qobject_to_qdict(arg);
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index b4ae1be..d135847 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -899,10 +899,12 @@ Example:
             goto out_obj;
         }
         visit_type_UserDefOne_members(v, *obj, &err);
-        error_propagate(errp, err);
-        err = NULL;
+        if (err) {
+            goto out_obj;
+        }
+        visit_check_struct(v, &err);
     out_obj:
-        visit_end_struct(v, &err);
+        visit_end_struct(v);
     out:
         error_propagate(errp, err);
     }
@@ -1007,7 +1009,10 @@ Example:
             goto out;
         }
         visit_type_UserDefOneList(v, "arg1", &arg1, &err);
-        visit_end_struct(v, err ? NULL : &err);
+        if (!err) {
+            visit_check_struct(v, &err);
+        }
+        visit_end_struct(v);
         if (err) {
             goto out;
         }
@@ -1026,7 +1031,7 @@ Example:
         v = qapi_dealloc_get_visitor(qdv);
         visit_start_struct(v, NULL, NULL, 0, NULL);
         visit_type_UserDefOneList(v, "arg1", &arg1, NULL);
-        visit_end_struct(v, NULL);
+        visit_end_struct(v);
         qapi_dealloc_visitor_cleanup(qdv);
     }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 20/24] qapi: Don't pass NULL to printf in string input visitor
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (18 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 19/24] qapi: Split visit_end_struct() into pieces Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-29  9:03   ` Markus Armbruster
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 21/24] tests/string-input-visitor: Add negative integer tests Eric Blake
                   ` (4 subsequent siblings)
  24 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Make sure the error message for visit_type_uint64() gracefully
handles a NULL 'name' when called from the top level or a list
context, as not all the world behaves like glibc in allowing
NULL through a printf-family %s.

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

---
v16: split off NULL handling
v15: new patch
---
 qapi/string-input-visitor.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 797973a..b55d199 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -222,7 +222,7 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
     return;

 error:
-    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
+    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
                "an int64 value or range");
 }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 21/24] tests/string-input-visitor: Add negative integer tests
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (19 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 20/24] qapi: Don't pass NULL to printf in string input visitor Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 22/24] qapi: Fix string input visitor handling of invalid list Eric Blake
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

From: Markus Armbruster <armbru@redhat.com>

Add two negative tests, one for int and one for int16List.  The latter
exposes a bug: nonsensical input results in an empty list instead of
an error.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1461325048-14122-1-git-send-email-armbru@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>

---
v15: new patch
---
 tests/test-string-input-visitor.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 9e6906a..8114908 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -63,6 +63,13 @@ static void test_visitor_in_int(TestInputVisitorData *data,
     visit_type_int(v, NULL, &res, &err);
     g_assert(!err);
     g_assert_cmpint(res, ==, value);
+
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "not an int");
+
+    visit_type_int(v, NULL, &res, &err);
+    error_free_or_abort(&err);
 }

 static void test_visitor_in_intList(TestInputVisitorData *data,
@@ -70,6 +77,7 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
 {
     int64_t value[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20};
     int16List *res = NULL, *tmp;
+    Error *err = NULL;
     Visitor *v;
     int i = 0;

@@ -90,6 +98,13 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
         g_free(tmp);
         tmp = res;
     }
+
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "not an int list");
+
+    visit_type_int16List(v, NULL, &res, &err);
+    /* FIXME fix the visitor, then error_free_or_abort(&err) here */
 }

 static void test_visitor_in_bool(TestInputVisitorData *data,
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 22/24] qapi: Fix string input visitor handling of invalid list
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (20 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 21/24] tests/string-input-visitor: Add negative integer tests Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 23/24] qapi: Simplify semantics of visit_next_list() Eric Blake
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

As shown in the previous commit, the string input visitor was
treating bogus input as an empty list rather than an error.
Fix parse_str() to set errp, then the callers to exit early if
an error was reported.

Meanwhile, fix the testsuite to use the generated
qapi_free_int16List() instead of rolling our own, and to
validate the fixed behavior, while at the same time documenting
one more change that we'd like to make in a later patch (a
failed visit_start_list should guarantee a NULL pointer,
regardless of what things were on input).

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

---
v16: split off NULL fix
v15: new patch
---
 qapi/string-input-visitor.c       | 17 ++++++++++++-----
 tests/test-string-input-visitor.c | 12 +++++-------
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index b55d199..ad150dc 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -44,7 +44,7 @@ static void free_range(void *range, void *dummy)
     g_free(range);
 }

-static void parse_str(StringInputVisitor *siv, Error **errp)
+static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
 {
     char *str = (char *) siv->string;
     long long start, end;
@@ -52,7 +52,7 @@ static void parse_str(StringInputVisitor *siv, Error **errp)
     char *endptr;

     if (siv->ranges) {
-        return;
+        return 0;
     }

     do {
@@ -117,11 +117,14 @@ static void parse_str(StringInputVisitor *siv, Error **errp)
         }
     } while (str);

-    return;
+    return 0;
 error:
     g_list_foreach(siv->ranges, free_range, NULL);
     g_list_free(siv->ranges);
     siv->ranges = NULL;
+    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+               "an int64 value or range");
+    return -1;
 }

 static void
@@ -129,7 +132,9 @@ start_list(Visitor *v, const char *name, Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);

-    parse_str(siv, errp);
+    if (parse_str(siv, name, errp) < 0) {
+        return;
+    }

     siv->cur_range = g_list_first(siv->ranges);
     if (siv->cur_range) {
@@ -195,7 +200,9 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
         return;
     }

-    parse_str(siv, errp);
+    if (parse_str(siv, name, errp) < 0) {
+        return;
+    }

     if (!siv->ranges) {
         goto error;
diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 8114908..f99824d 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -92,19 +92,17 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
     }
     g_assert(!tmp);

-    tmp = res;
-    while (tmp) {
-        res = res->next;
-        g_free(tmp);
-        tmp = res;
-    }
+    qapi_free_int16List(res);

     visitor_input_teardown(data, unused);

     v = visitor_input_test_init(data, "not an int list");

+    /* FIXME: res should be NULL on failure, regardless of starting value */
+    res = NULL;
     visit_type_int16List(v, NULL, &res, &err);
-    /* FIXME fix the visitor, then error_free_or_abort(&err) here */
+    error_free_or_abort(&err);
+    g_assert(!res);
 }

 static void test_visitor_in_bool(TestInputVisitorData *data,
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 23/24] qapi: Simplify semantics of visit_next_list()
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (21 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 22/24] qapi: Fix string input visitor handling of invalid list Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 24/24] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
  2016-04-29 11:13 ` [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, David Gibson, Alexander Graf, open list:sPAPR

The semantics of the list visit are somewhat baroque, with the
following pseudocode when FooList is used:

start()
for (prev = head; cur = next(prev); prev = &cur) {
    visit(&cur->value)
}

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.

Thankfully, we only 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, so we can refactor how
lists are laid out with minimal churn among clients.

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)
for (tail = *head; tail; tail = next(tail)) {
    visit(&tail->value)
}

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()).  As a minor drawback, we now allocate in
two functions instead of one, and have to pass the size to
both functions (unless we were to tweak the input visitors to
cache the size to start_list for reuse during next_list, but
that defeats the goal of less visitor state).

The signature of visit_start_list() is chosen to match
visit_start_struct(), with the new parameters after 'name'.

The spapr_drc case is a virtual visit, 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 (several prerequisite patches
refactored things to make this patch straightforward).  But it
turned out that the string and opts visitors munge enough other
state during visit_next_list() to make it easier to just
document and require a GenericList visit for now; an assertion
will remind us to adjust things if we need the semantics in the
future.

Several pre-requisite cleanup patches made the reshuffling of
the various visitors easier; particularly the qmp input visitor.

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

---
v16: add one more assert
v15: address review comments, tweak commit message, rebase atop
fix for string-input list bug, have qmp input push() return entry
rather than taking another parameter
v14: no change
v13: documentation wording tweaks
v12: rebase to earlier changes, drop R-b, improve documentation,
split out qmp-input cleanups into prereq patches
[no v10, v11]
v9: no change
v8: consistent parameter order, fix qmp_input_get_next_type() to not
skip list entries
v7: new patch
---
 include/qapi/visitor.h               | 49 +++++++++++++++++++-----------------
 include/qapi/visitor-impl.h          |  8 +++---
 scripts/qapi-visit.py                | 16 ++++++------
 include/qapi/opts-visitor.h          |  3 ++-
 include/qapi/string-input-visitor.h  |  3 ++-
 include/qapi/string-output-visitor.h |  3 ++-
 qapi/qapi-visit-core.c               | 18 +++++++++----
 hw/ppc/spapr_drc.c                   |  2 +-
 qapi/opts-visitor.c                  | 33 +++++++++++-------------
 qapi/qapi-dealloc-visitor.c          | 35 ++++++--------------------
 qapi/qmp-input-visitor.c             | 40 +++++++++++++++--------------
 qapi/qmp-output-visitor.c            | 22 ++++------------
 qapi/string-input-visitor.c          | 29 +++++++++------------
 qapi/string-output-visitor.c         | 41 +++++++++++-------------------
 tests/test-string-input-visitor.c    |  2 --
 docs/qapi-code-gen.txt               | 16 ++++++------
 16 files changed, 142 insertions(+), 178 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index a7a0cb8..3095455 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -176,7 +176,7 @@
  *  if (err) {
  *      goto outobj;
  *  }
- *  visit_start_list(v, "list", &err);
+ *  visit_start_list(v, "list", NULL, 0, &err);
  *  if (err) {
  *      goto outobj;
  *  }
@@ -280,19 +280,27 @@ void visit_end_struct(Visitor *v);
  * @name expresses the relationship of this list to its parent
  * container; see the general description of @name above.
  *
+ * @list must be non-NULL for a real walk, in which case @size
+ * determines how much memory an input visitor will allocate into
+ * *@list (at least sizeof(GenericList)).  Some visitors also allow
+ * @list to be NULL for a virtual walk, in which case @size is
+ * ignored.
+ *
  * @errp obeys typical error usage, and reports failures such as a
- * member @name is not present, or present but not a list.
+ * member @name is not present, or present but not a list.  On error,
+ * input visitors set *@list to NULL.
  *
  * After visit_start_list() succeeds, the caller may visit its members
- * one after the other.  A real visit uses visit_next_list() for
- * traversing the linked list, while a virtual visit uses other means.
- * For each list element, call the appropriate visit_type_FOO() with
- * name set to NULL and obj set to the address of the value member of
- * the list element.  Finally, visit_end_list() needs to be called to
- * clean up, even if intermediate visits fail.  See the examples
- * above.
+ * one after the other.  A real visit (where @obj is non-NULL) uses
+ * visit_next_list() for traversing the linked list, while a virtual
+ * visit (where @obj is NULL) uses other means.  For each list
+ * element, call the appropriate visit_type_FOO() with name set to
+ * NULL and obj set to the address of the value member of the list
+ * element.  Finally, visit_end_list() needs to be called to clean up,
+ * even if intermediate visits fail.  See the examples above.
  */
-void visit_start_list(Visitor *v, const char *name, Error **errp);
+void visit_start_list(Visitor *v, const char *name, GenericList **list,
+                      size_t size, Error **errp);

 /*
  * Iterate over a GenericList during a non-virtual list visit.
@@ -300,20 +308,15 @@ void visit_start_list(Visitor *v, const char *name, Error **errp);
  * @size represents the size of a linked list node (at least
  * sizeof(GenericList)).
  *
- * @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.  Should be called in a loop until a
- * NULL return or error occurs; for each non-NULL return, the caller
- * then calls the appropriate visit_type_*() for the element type
- * of the list, with that function's name parameter set to NULL and
- * obj set to the address of (*@list)->value.
- *
- * FIXME: This interface is awkward; it requires all callbacks to
- * track whether it is the first or a subsequent call.  A better
- * interface would pass the head of the list through
- * visit_start_list().
+ * @tail must not be NULL; on the first call, @tail is the value of
+ * *list after visit_start_list(), and on subsequent calls @tail must
+ * be the previously returned value.  Should be called in a loop until
+ * a NULL return or error occurs; for each non-NULL return, the caller
+ * then calls the appropriate visit_type_*() for the element type of
+ * the list, with that function's name parameter set to NULL and obj
+ * set to the address of @tail->value.
  */
-GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size);
+GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);

 /*
  * Complete a list visit started earlier.
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index b20a922..145afd0 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -49,11 +49,13 @@ struct Visitor
     /* Must be set to visit structs */
     void (*end_struct)(Visitor *v);

-    /* Must be set */
-    void (*start_list)(Visitor *v, const char *name, Error **errp);
+    /* Must be set; implementations may require @list to be non-null,
+     * but must document it. */
+    void (*start_list)(Visitor *v, const char *name, GenericList **list,
+                       size_t size, Error **errp);

     /* Must be set */
-    GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size);
+    GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);

     /* Must be set */
     void (*end_list)(Visitor *v);
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index bdf8971..8b7efcc 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -117,18 +117,20 @@ def gen_visit_list(name, element_type):
 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 {
     Error *err = NULL;
-    GenericList *i, **prev;
+    %(c_name)s *tail;
+    size_t size = sizeof(**obj);

-    visit_start_list(v, name, &err);
+    visit_start_list(v, name, (GenericList **)obj, size, &err);
     if (err) {
         goto out;
     }

-    for (prev = (GenericList **)obj;
-         !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
-         prev = &i) {
-        %(c_name)s *native_i = (%(c_name)s *)i;
-        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
+    for (tail = *obj; tail;
+         tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) {
+        visit_type_%(c_elt_type)s(v, NULL, &tail->value, &err);
+        if (err) {
+            break;
+        }
     }

     visit_end_list(v);
diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h
index fe37ed9..ae1bf7c 100644
--- a/include/qapi/opts-visitor.h
+++ b/include/qapi/opts-visitor.h
@@ -32,7 +32,8 @@ typedef struct OptsVisitor OptsVisitor;
  *
  * The Opts input visitor does not implement support for visiting QAPI
  * alternates, numbers (other than integers), null, or arbitrary
- * QTypes.
+ * QTypes.  It also requires a non-null list argument to
+ * visit_start_list().
  */
 OptsVisitor *opts_visitor_new(const QemuOpts *opts);
 void opts_visitor_cleanup(OptsVisitor *nv);
diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h
index a8d8f67..7b76c2b 100644
--- a/include/qapi/string-input-visitor.h
+++ b/include/qapi/string-input-visitor.h
@@ -19,7 +19,8 @@ typedef struct StringInputVisitor StringInputVisitor;

 /*
  * The string input visitor does not implement support for visiting
- * QAPI structs, alternates, null, or arbitrary QTypes.
+ * QAPI structs, alternates, null, or arbitrary QTypes.  It also
+ * requires a non-null list argument to visit_start_list().
  */
 StringInputVisitor *string_input_visitor_new(const char *str);
 void string_input_visitor_cleanup(StringInputVisitor *v);
diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
index 89b7e4b..e10522a 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -19,7 +19,8 @@ typedef struct StringOutputVisitor StringOutputVisitor;

 /*
  * The string output visitor does not implement support for visiting
- * QAPI structs, alternates, null, or arbitrary QTypes.
+ * QAPI structs, alternates, null, or arbitrary QTypes.  It also
+ * requires a non-null list argument to visit_start_list().
  */
 StringOutputVisitor *string_output_visitor_new(bool human);
 void string_output_visitor_cleanup(StringOutputVisitor *v);
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 9b99c02..2aa9137 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -48,15 +48,23 @@ void visit_end_struct(Visitor *v)
     v->end_struct(v);
 }

-void visit_start_list(Visitor *v, const char *name, Error **errp)
+void visit_start_list(Visitor *v, const char *name, GenericList **list,
+                      size_t size, Error **errp)
 {
-    v->start_list(v, name, errp);
+    Error *err = NULL;
+
+    assert(!list || size >= sizeof(GenericList));
+    v->start_list(v, name, list, size, &err);
+    if (list && v->type == VISITOR_INPUT) {
+        assert(!(err && *list));
+    }
+    error_propagate(errp, err);
 }

-GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size)
+GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size)
 {
-    assert(list && size >= sizeof(GenericList));
-    return v->next_list(v, list, size);
+    assert(tail && size >= sizeof(GenericList));
+    return v->next_list(v, tail, size);
 }

 void visit_end_list(Visitor *v)
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index e3a6191..94c875d 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -309,7 +309,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
             int i;
             prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
             name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
-            visit_start_list(v, name, &err);
+            visit_start_list(v, name, NULL, 0, &err);
             if (err) {
                 error_propagate(errp, err);
                 return;
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index ece0598..4cf1cf8 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -23,9 +23,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
@@ -214,35 +213,33 @@ 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, size_t size,
+                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 a list */
+    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_malloc0(size);
+    } else {
+        *list = NULL;
     }
 }


 static GenericList *
-opts_next_list(Visitor *v, GenericList **list, size_t size)
+opts_next_list(Visitor *v, GenericList *tail, size_t size)
 {
     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;
@@ -263,7 +260,6 @@ opts_next_list(Visitor *v, GenericList **list, size_t size)
             g_hash_table_remove(ov->unprocessed_opts, opt->name);
             return NULL;
         }
-        link = &(*list)->next;
         break;
     }

@@ -271,8 +267,8 @@ opts_next_list(Visitor *v, GenericList **list, size_t size)
         abort();
     }

-    *link = g_malloc0(size);
-    return *link;
+    tail->next = g_malloc0(size);
+    return tail->next;
 }


@@ -281,8 +277,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 9005bad..cd68b55 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -22,7 +22,6 @@
 typedef struct StackEntry
 {
     void *value;
-    bool is_list_head;
     QTAILQ_ENTRY(StackEntry) node;
 } StackEntry;

@@ -43,10 +42,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);
 }

@@ -93,38 +88,22 @@ static void qapi_dealloc_end_alternate(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, size_t size,
+                                    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 *tail,
                                            size_t size)
 {
-    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 = tail->next;
+    g_free(tail);
+    return next;
 }

 static void qapi_dealloc_end_list(Visitor *v)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    void *obj = qapi_dealloc_pop(qov);
-    assert(obj == NULL); /* should've been list head tracker with no payload */
 }

 static void qapi_dealloc_type_str(Visitor *v, const char *name, char **obj,
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 90b9df1..aea90a1 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -29,7 +29,6 @@ typedef struct StackObject

     GHashTable *h;           /* If obj is dict: unvisited keys */
     const QListEntry *entry; /* If obj is list: unvisited tail */
-    bool first;              /* If obj is list: next_list() not yet called? */
 } StackObject;

 struct QmpInputVisitor
@@ -81,7 +80,6 @@ static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
     } else {
         assert(qobject_type(qobj) == QTYPE_QLIST);
         assert(!name);
-        assert(!tos->first);
         ret = qlist_entry_obj(tos->entry);
         if (consume) {
             tos->entry = qlist_next(tos->entry);
@@ -97,7 +95,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 const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj,
+                                        Error **errp)
 {
     GHashTable *h;
     StackObject *tos = &qiv->stack[qiv->nb_stack];
@@ -105,7 +104,7 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
     assert(obj);
     if (qiv->nb_stack >= QIV_STACK_SIZE) {
         error_setg(errp, "An internal buffer overran");
-        return;
+        return NULL;
     }

     tos->obj = obj;
@@ -118,10 +117,10 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
         tos->h = h;
     } else if (qobject_type(obj) == QTYPE_QLIST) {
         tos->entry = qlist_first(qobject_to_qlist(obj));
-        tos->first = true;
     }

     qiv->nb_stack++;
+    return tos->entry;
 }


@@ -192,40 +191,43 @@ static void qmp_input_start_struct(Visitor *v, const char *name, 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, size_t size, 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) {
+        if (list) {
+            *list = NULL;
+        }
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "list");
         return;
     }

-    qmp_input_push(qiv, qobj, errp);
+    entry = qmp_input_push(qiv, qobj, errp);
+    if (list) {
+        if (entry) {
+            *list = g_malloc0(size);
+        } else {
+            *list = NULL;
+        }
+    }
 }

-static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
+static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail,
                                         size_t size)
 {
     QmpInputVisitor *qiv = to_qiv(v);
-    GenericList *entry;
     StackObject *so = &qiv->stack[qiv->nb_stack - 1];

     if (!so->entry) {
         return NULL;
     }
-
-    entry = g_malloc0(size);
-    if (so->first) {
-        *list = entry;
-        so->first = false;
-    } else {
-        (*list)->next = entry;
-    }
-
-    return entry;
+    tail->next = g_malloc0(size);
+    return tail->next;
 }


diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index a66d754..ef9f62c 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -22,7 +22,6 @@
 typedef struct QStackEntry
 {
     QObject *value;
-    bool is_list_head;
     QTAILQ_ENTRY(QStackEntry) node;
 } QStackEntry;

@@ -52,9 +51,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);
 }

@@ -118,7 +114,9 @@ static void qmp_output_end_struct(Visitor *v)
     assert(qobject_type(value) == QTYPE_QDICT);
 }

-static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
+static void qmp_output_start_list(Visitor *v, const char *name,
+                                  GenericList **listp, size_t size,
+                                  Error **errp)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QList *list = qlist_new();
@@ -127,20 +125,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 *tail,
                                          size_t size)
 {
-    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 tail->next;
 }

 static void qmp_output_end_list(Visitor *v)
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index ad150dc..30b5879 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -25,8 +25,6 @@ struct StringInputVisitor
 {
     Visitor visitor;

-    bool head;
-
     GList *ranges;
     GList *cur_range;
     int64_t cur;
@@ -128,11 +126,16 @@ error:
 }

 static void
-start_list(Visitor *v, const char *name, Error **errp)
+start_list(Visitor *v, const char *name, GenericList **list, size_t size,
+           Error **errp)
 {
     StringInputVisitor *siv = to_siv(v);

+    /* We don't support visits without a list */
+    assert(list);
+
     if (parse_str(siv, name, errp) < 0) {
+        *list = NULL;
         return;
     }

@@ -142,13 +145,15 @@ start_list(Visitor *v, const char *name, Error **errp)
         if (r) {
             siv->cur = r->begin;
         }
+        *list = g_malloc0(size);
+    } else {
+        *list = NULL;
     }
 }

-static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
+static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
 {
     StringInputVisitor *siv = to_siv(v);
-    GenericList **link;
     Range *r;

     if (!siv->ranges || !siv->cur_range) {
@@ -172,21 +177,12 @@ static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
         siv->cur = r->begin;
     }

-    if (siv->head) {
-        link = list;
-        siv->head = false;
-    } else {
-        link = &(*list)->next;
-    }
-
-    *link = g_malloc0(size);
-    return *link;
+    tail->next = g_malloc0(size);
+    return tail->next;
 }

 static void end_list(Visitor *v)
 {
-    StringInputVisitor *siv = to_siv(v);
-    siv->head = true;
 }

 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
@@ -369,6 +365,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 0d44d7e..d013196 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -20,7 +20,7 @@

 enum ListMode {
     LM_NONE,             /* not traversing a list of repeated options */
-    LM_STARTED,          /* start_list() succeeded */
+    LM_STARTED,          /* next_list() ready to be called */

     LM_IN_PROGRESS,      /* next_list() has been called.
                           *
@@ -48,7 +48,7 @@ enum ListMode {

     LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */

-    LM_END
+    LM_END,              /* next_list() called, about to see last element. */
 };

 typedef enum ListMode ListMode;
@@ -58,7 +58,6 @@ struct StringOutputVisitor
     Visitor visitor;
     bool human;
     GString *string;
-    bool head;
     ListMode list_mode;
     union {
         int64_t s;
@@ -266,39 +265,29 @@ static void print_type_number(Visitor *v, const char *name, double *obj,
 }

 static void
-start_list(Visitor *v, const char *name, Error **errp)
+start_list(Visitor *v, const char *name, GenericList **list, size_t size,
+           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 list */
+    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, size_t size)
+static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
 {
     StringOutputVisitor *sov = to_sov(v);
-    GenericList *ret = NULL;
-    if (*list) {
-        if (sov->head) {
-            ret = *list;
-        } else {
-            ret = (*list)->next;
-        }
+    GenericList *ret = tail->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;
 }

@@ -311,8 +300,6 @@ static void 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/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index f99824d..5a56920 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -98,8 +98,6 @@ static void test_visitor_in_intList(TestInputVisitorData *data,

     v = visitor_input_test_init(data, "not an int list");

-    /* FIXME: res should be NULL on failure, regardless of starting value */
-    res = NULL;
     visit_type_int16List(v, NULL, &res, &err);
     error_free_or_abort(&err);
     g_assert(!res);
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index d135847..fe61c5c 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -912,18 +912,20 @@ Example:
     void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp)
     {
         Error *err = NULL;
-        GenericList *i, **prev;
+        UserDefOneList *tail;
+        size_t size = sizeof(**obj);

-        visit_start_list(v, name, &err);
+        visit_start_list(v, name, (GenericList **)obj, size, &err);
         if (err) {
             goto out;
         }

-        for (prev = (GenericList **)obj;
-             !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
-             prev = &i) {
-            UserDefOneList *native_i = (UserDefOneList *)i;
-            visit_type_UserDefOne(v, NULL, &native_i->value, &err);
+        for (tail = *obj; tail;
+             tail = (UserDefOneList *)visit_next_list(v, (GenericList *)tail, size)) {
+            visit_type_UserDefOne(v, NULL, &tail->value, &err);
+            if (err) {
+                break;
+            }
         }

         visit_end_list(v);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v16 24/24] qapi: Change visit_type_FOO() to no longer return partial objects
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (22 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 23/24] qapi: Simplify semantics of visit_next_list() Eric Blake
@ 2016-04-28 21:45 ` Eric Blake
  2016-04-29 11:13 ` [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster
  24 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-04-28 21:45 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.  We already fixed things in an earlier
patch to guarantee NULL if visit_start fails ("qapi: Guarantee
NULL obj on input visitor callback error"), but that does not
help the case where visit_start succeeds but some other failure
happens before visit_end, such that we leak a partially constructed
object outside visit_type_FOO(). 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), so callers can now unconditionally use
qapi_free_FOO() to clean up regardless of whether an error occurred.

The decision is done by adding visit_is_input(), then updating the
generated code to check if additional cleanup is needed based on
the type of visitor in use.

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>

---
v16: drop spurious hunk
v15: rebase, redo semantics on how generated code learns
about input visitors, hoist unrelated assertions earlier
v14: no change
v13: rebase to latest, documentation tweaks
v12: rebase to latest, don't modify callback signatures, use newer
approach for detecting input visitors, avoid 'allocated' boolean
[no v10, v11]
v9: fix bug in use of errp
v8: rebase to earlier changes
v7: rebase to earlier changes, enhance commit message, also fix
visit_type_str() and visit_type_any()
v6: rebase on top of earlier doc and formatting improvements, mention
that *obj can be uninitialized on entry to an input visitor, rework
semantics to keep valgrind happy on uninitialized input, break some
long lines
---
 include/qapi/visitor.h         | 25 ++++++++++++++++---------
 scripts/qapi-visit.py          | 22 +++++++++++++---------
 qapi/qapi-visit-core.c         |  5 +++++
 tests/test-qmp-commands.c      | 13 ++++++-------
 tests/test-qmp-input-strict.c  | 17 +++++++----------
 tests/test-qmp-input-visitor.c | 10 ++--------
 docs/qapi-code-gen.txt         |  8 ++++++++
 7 files changed, 57 insertions(+), 43 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 3095455..a430c19 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -66,12 +66,14 @@
  * member @name is not present, or is present but not the specified
  * type).
  *
- * FIXME: At present, visit_type_FOO() is an awkward interface: input
- * visitors may allocate an incomplete *@obj even when reporting an
- * error, but using an output visitor with an incomplete object has
- * undefined behavior.  To avoid a memory leak, callers must use
- * qapi_free_FOO() even on error (this uses the dealloc visitor, and
- * safely handles an incomplete object).
+ * If an error is detected during visit_type_FOO() with an input
+ * visitor, then *@obj will be NULL for pointer types, and left
+ * unchanged for scalar types.  Using an output visitor with an
+ * incomplete object has undefined behavior (other than a special case
+ * for visit_type_str() treating NULL like ""), while the dealloc
+ * visitor safely handles incomplete objects.  Since input visitors
+ * never produce an incomplete object, such an object is possible only
+ * by manual construction.
  *
  * For the QAPI object types (structs, unions, and alternates), there
  * is an additional generated function in qapi-visit.h compatible
@@ -106,7 +108,6 @@
  *  v = ...obtain input visitor...
  *  visit_type_Foo(v, NULL, &f, &err);
  *  if (err) {
- *      qapi_free_Foo(f);
  *      ...handle error...
  *  } else {
  *      ...use f...
@@ -124,7 +125,6 @@
  *  v = ...obtain input visitor...
  *  visit_type_FooList(v, NULL, &l, &err);
  *  if (err) {
- *      qapi_free_FooList(l);
  *      ...handle error...
  *  } else {
  *      for ( ; l; l = l->next) {
@@ -154,7 +154,9 @@
  * helpers that rely on in-tree information to control the walk:
  * visit_optional() for the 'has_member' field associated with
  * optional 'member' in the C struct; and visit_next_list() for
- * advancing through a FooList linked list.  Only the generated
+ * advancing through a FooList linked list.  Similarly, the
+ * visit_is_input() helper makes it possible to write code that is
+ * visitor-agnostic everywhere except for cleanup.  Only the generated
  * visit_type functions need to use these helpers.
  *
  * It is also possible to use the visitors to do a virtual walk, where
@@ -404,6 +406,11 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
 void visit_type_enum(Visitor *v, const char *name, int *obj,
                      const char *const strings[], Error **errp);

+/*
+ * Check if visitor is an input visitor.
+ */
+bool visit_is_input(Visitor *v);
+
 /*** Visiting built-in types ***/

 /*
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 8b7efcc..70ea8ca 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -108,10 +108,6 @@ out:


 def gen_visit_list(name, element_type):
-    # FIXME: if *obj is NULL on entry, and the first visit_next_list()
-    # assigns to *obj, while a later one fails, we should clean up *obj
-    # rather than leaving it non-NULL. As currently written, the caller must
-    # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
     return mcgen('''

 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
@@ -134,6 +130,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     }

     visit_end_list(v);
+    if (err && visit_is_input(v)) {
+        qapi_free_%(c_name)s(*obj);
+        *obj = NULL;
+    }
 out:
     error_propagate(errp, err);
 }
@@ -211,20 +211,20 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
                    "%(name)s");
     }
     visit_end_alternate(v);
+    if (err && visit_is_input(v)) {
+        qapi_free_%(c_name)s(*obj);
+        *obj = NULL;
+    }
 out:
     error_propagate(errp, err);
 }
 ''',
-                 name=name)
+                 name=name, c_name=c_name(name))

     return ret


 def gen_visit_object(name, base, members, variants):
-    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
-    # *obj, but then visit_type_FOO_members() 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.
     return mcgen('''

 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
@@ -245,6 +245,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
     visit_check_struct(v, &err);
 out_obj:
     visit_end_struct(v);
+    if (err && visit_is_input(v)) {
+        qapi_free_%(c_name)s(*obj);
+        *obj = NULL;
+    }
 out:
     error_propagate(errp, err);
 }
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 2aa9137..f5d4b52 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -104,6 +104,11 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
     return *present;
 }

+bool visit_is_input(Visitor *v)
+{
+    return v->type == VISITOR_INPUT;
+}
+
 void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
 {
     assert(obj);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 597fb44..5c3edd7 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -228,14 +228,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 2b053a2..4602529 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -182,10 +182,7 @@ static void test_validate_fail_struct(TestInputVisitorData *data,

     visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
-    if (p) {
-        g_free(p->string);
-    }
-    g_free(p);
+    g_assert(!p);
 }

 static void test_validate_fail_struct_nested(TestInputVisitorData *data,
@@ -199,7 +196,7 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data,

     visit_type_UserDefTwo(v, NULL, &udp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefTwo(udp);
+    g_assert(!udp);
 }

 static void test_validate_fail_list(TestInputVisitorData *data,
@@ -213,7 +210,7 @@ static void test_validate_fail_list(TestInputVisitorData *data,

     visit_type_UserDefOneList(v, NULL, &head, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefOneList(head);
+    g_assert(!head);
 }

 static void test_validate_fail_union_native_list(TestInputVisitorData *data,
@@ -228,7 +225,7 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data,

     visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefNativeListUnion(tmp);
+    g_assert(!tmp);
 }

 static void test_validate_fail_union_flat(TestInputVisitorData *data,
@@ -242,7 +239,7 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data,

     visit_type_UserDefFlatUnion(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefFlatUnion(tmp);
+    g_assert(!tmp);
 }

 static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
@@ -257,7 +254,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,

     visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefFlatUnion2(tmp);
+    g_assert(!tmp);
 }

 static void test_validate_fail_alternate(TestInputVisitorData *data,
@@ -271,7 +268,7 @@ static void test_validate_fail_alternate(TestInputVisitorData *data,

     visit_type_UserDefAlternate(v, NULL, &tmp, &err);
     error_free_or_abort(&err);
-    qapi_free_UserDefAlternate(tmp);
+    g_assert(!tmp);
 }

 static void do_test_validate_qmp_introspect(TestInputVisitorData *data,
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 6617276..cee07ce 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -773,18 +773,12 @@ static void test_visitor_in_errors(TestInputVisitorData *data,

     visit_type_TestStruct(v, NULL, &p, &err);
     error_free_or_abort(&err);
-    /* FIXME - a failed parse should not leave a partially-allocated p
-     * for us to clean up; this could cause callers to leak memory. */
-    g_assert(p->string == NULL);
-
-    g_free(p->string);
-    g_free(p);
+    g_assert(!p);

     v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]");
     visit_type_strList(v, NULL, &q, &err);
     error_free_or_abort(&err);
-    assert(q);
-    qapi_free_strList(q);
+    assert(!q);
 }

 static void test_visitor_in_wrong_type(TestInputVisitorData *data,
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index fe61c5c..d7d6987 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -905,6 +905,10 @@ Example:
         visit_check_struct(v, &err);
     out_obj:
         visit_end_struct(v);
+        if (err && visit_is_input(v)) {
+            qapi_free_UserDefOne(*obj);
+            *obj = NULL;
+        }
     out:
         error_propagate(errp, err);
     }
@@ -929,6 +933,10 @@ Example:
         }

         visit_end_list(v);
+        if (err && visit_is_input(v)) {
+            qapi_free_UserDefOneList(*obj);
+            *obj = NULL;
+        }
     out:
         error_propagate(errp, err);
     }
-- 
2.5.5

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

* Re: [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error Eric Blake
@ 2016-04-29  8:28   ` Markus Armbruster
  2016-04-29 12:10     ` Eric Blake
  0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29  8:28 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Our existing input visitors were not very consistent on errors
> in a function taking 'TYPE **obj' (that is, start_struct(),
> start_alternate(), type_str(), and type_any(). next_list() is
> similar, except that since commit 08f9541, it can't fail).

Multiple sentences in a parenthesis, ugh :)

    Our existing input visitors were not very consistent on errors in a
    function taking 'TYPE **obj'.  These are start_struct(),
    start_alternate(), type_str(), and type_any().  next_list() is
    similar, but can't fail (see commit since 08f9541).

Can touch up on commit.

> While all of them set '*obj' to allocated storage on success,
> it was not obvious whether '*obj' was guaranteed safe on failure,
> or whether it was left uninitialized.  But a future patch wants
> to guarantee that visit_type_FOO() does not leak a partially-
> constructed obj back to the caller; it is easier to implement
> this if we can reliably state that input visitors assign '*obj'
> regardless of success or failure, and that on failure *obj is
> NULL.  Add assertions to enforce consistency in the final
> setting of err vs. *obj.
>
> The opts-visitor start_struct() doesn't set an error, but it
> also was doing a weird check for 0 size; all callers pass in
> non-zero size if obj is non-NULL.
>
> The testsuite has at least one spot where we no longer need
> to pre-initialize a variable prior to a visit; valgrind confirms
> that the test is still fine with the cleanup.
>
> A later patch will document the design constraint implemented
> here.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v16: tighten assertions, another commit message tweak
> v15: enhance commit message, hoist assertions from later in series
> v14: no change
> v13: no change
> v12: new patch
> ---
>  qapi/qapi-visit-core.c        | 34 ++++++++++++++++++++++++++++++----
>  qapi/opts-visitor.c           |  3 ++-
>  qapi/qmp-input-visitor.c      |  4 ++++
>  qapi/string-input-visitor.c   |  1 +
>  tests/test-qmp-input-strict.c |  2 +-
>  5 files changed, 38 insertions(+), 6 deletions(-)
>
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 3cd7edc..7ad5ff4 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -23,7 +23,13 @@
>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp)
>  {
> -    v->start_struct(v, name, obj, size, errp);
> +    Error *err = NULL;
> +
> +    v->start_struct(v, name, obj, size, &err);
> +    if (obj && v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);
> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_end_struct(Visitor *v, Error **errp)
> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>                             GenericAlternate **obj, size_t size,
>                             bool promote_int, Error **errp)
>  {
> +    Error *err = NULL;
> +
>      assert(obj && size >= sizeof(GenericAlternate));
>      if (v->start_alternate) {
> -        v->start_alternate(v, name, obj, size, promote_int, errp);
> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>      }
> +    if (v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);

Could assert(v->start_alternate && !err != !*obj), to preempt "what if
!v->start_alternate" worries.  If you like that, I can do it on commit.

> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_end_alternate(Visitor *v)
> @@ -188,7 +200,14 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
>
>  void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>  {
> -    v->type_str(v, name, obj, errp);
> +    Error *err = NULL;
> +
> +    assert(obj);
> +    v->type_str(v, name, obj, &err);
> +    if (v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);
> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_type_number(Visitor *v, const char *name, double *obj,
> @@ -199,7 +218,14 @@ void visit_type_number(Visitor *v, const char *name, double *obj,
>
>  void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
>  {
> -    v->type_any(v, name, obj, errp);
> +    Error *err = NULL;
> +
> +    assert(obj);
> +    v->type_any(v, name, obj, &err);
> +    if (v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);
> +    }
> +    error_propagate(errp, err);
>  }
>
>  static void output_type_enum(Visitor *v, const char *name, int *obj,
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 66aeaed..4cb6436 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -133,7 +133,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>      const QemuOpt *opt;
>
>      if (obj) {
> -        *obj = g_malloc0(size > 0 ? size : 1);
> +        *obj = g_malloc0(size);
>      }
>      if (ov->depth++ > 0) {
>          return;
> @@ -314,6 +314,7 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>
>      opt = lookup_scalar(ov, name, errp);
>      if (!opt) {
> +        *obj = NULL;
>          return;
>      }
>      *obj = g_strdup(opt->str ? opt->str : "");
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 02d4233..77cce8b 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -120,6 +120,9 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>      QObject *qobj = qmp_input_get_object(qiv, name, true);
>      Error *err = NULL;
>
> +    if (obj) {
> +        *obj = NULL;
> +    }
>      if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "QDict");
> @@ -267,6 +270,7 @@ static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
>      QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
>
>      if (!qstr) {
> +        *obj = NULL;
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "string");
>          return;
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index d604575..797973a 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -293,6 +293,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
>      if (siv->string) {
>          *obj = g_strdup(siv->string);
>      } else {
> +        *obj = NULL;
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "string");
>      }
> diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
> index d71727e..d5f80ec 100644
> --- a/tests/test-qmp-input-strict.c
> +++ b/tests/test-qmp-input-strict.c
> @@ -263,7 +263,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
>  static void test_validate_fail_alternate(TestInputVisitorData *data,
>                                           const void *unused)
>  {
> -    UserDefAlternate *tmp = NULL;
> +    UserDefAlternate *tmp;
>      Visitor *v;
>      Error *err = NULL;

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

* Re: [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced Eric Blake
@ 2016-04-29  8:50   ` Markus Armbruster
  2016-04-29 12:15     ` Eric Blake
  0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29  8:50 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> In the QMP input visitor, visiting a list traverses two objects:
> the QAPI GenericList of the caller (which gets advanced in
> visit_next_list() regardless of this patch), and the QList input
> that we are converting to QAPI.  For consistency with QDict
> visits, we want to consume elements from the input QList during
> the visit_type_FOO() for the list element; that is, we want ALL
> the code for consuming an input to live in qmp_input_get_object(),
> rather than having it split according to whether we are visiting
> a dict or a list.  Making qmp_input_get_object() the common point
> of consumption will make it easier for a later patch to refactor
> visit_start_list() to cover the GenericList * head of a QAPI list,
> and in turn will get rid of the 'first' flag (which lived in
> qmp_input_next_list() pre-patch, and is hoisted to StackObject
> by this patch).
>
> This patch is therefore shifting the post-condition use of
> 'entry' from:
>
>         start_list next_list type_ELT ... next_list type_ELT next_list end_list
>  visit                       1st elt                last elt
>  entry  NULL       1st elt   1st elt      last elt  last elt NULL      gone

You added a line "visit", but the introduction still only mentions
'entry'.  Mildly confusing.  If you have an idea on how to improve it, I
can touch it up on commit.

>
> where type_ELT() returns (entry ? entry : 1st elt) and next_list() steps
> entry
>
> to this usage:
>
>         start_list next_list type_ELT ... next_list type_ELT next_list end_list
>  visit                       1st elt                last elt
>  entry  1st elt    1nd elt   2nd elt      last elt  NULL     NULL      gone
>
> where type_ELT() steps entry and returns the old entry, and next_list()
> leaves entry alone.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

Patch looks good.

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

* Re: [Qemu-devel] [PATCH v16 20/24] qapi: Don't pass NULL to printf in string input visitor
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 20/24] qapi: Don't pass NULL to printf in string input visitor Eric Blake
@ 2016-04-29  9:03   ` Markus Armbruster
  0 siblings, 0 replies; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29  9:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Make sure the error message for visit_type_uint64() gracefully
> handles a NULL 'name' when called from the top level or a list
> context, as not all the world behaves like glibc in allowing
> NULL through a printf-family %s.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v16: split off NULL handling
> v15: new patch
> ---
>  qapi/string-input-visitor.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index 797973a..b55d199 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -222,7 +222,7 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
>      return;
>
>  error:
> -    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
> +    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
>                 "an int64 value or range");
>  }

Crash bug, nominating the fix for 2.6 (I'll post a pull request), or
else -stable.

Reviewed-by: Markus Armbruster <armbru@redhat.com>

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

* Re: [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
                   ` (23 preceding siblings ...)
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 24/24] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
@ 2016-04-29 11:13 ` Markus Armbruster
  2016-04-29 12:16   ` Eric Blake
  24 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29 11:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> 2.7 material; much less churn this time through, but enough
> that it was easier for me to repost than to make Markus do
> the touchups his review found.
>
> Based on master, with no prerequisite patches.
>
> Also available as a tag at this location:
> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv16e
>
> 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
>
> v15 was:
> https://lists.gnu.org/archive/html/qemu-devel/2016-04/msg04147.html
>
> Since then, I deferred the qmp-commands.hx patch to later
> (Marc-André's solution may be better), and split two others
> while addressing review comments.

Looks ready.  Thanks for the quick respin!

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

* Re: [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error
  2016-04-29  8:28   ` Markus Armbruster
@ 2016-04-29 12:10     ` Eric Blake
  2016-04-29 12:17       ` Eric Blake
  0 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-29 12:10 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 04/29/2016 02:28 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Our existing input visitors were not very consistent on errors
>> in a function taking 'TYPE **obj' (that is, start_struct(),
>> start_alternate(), type_str(), and type_any(). next_list() is
>> similar, except that since commit 08f9541, it can't fail).
> 
> Multiple sentences in a parenthesis, ugh :)
> 
>     Our existing input visitors were not very consistent on errors in a
>     function taking 'TYPE **obj'.  These are start_struct(),
>     start_alternate(), type_str(), and type_any().  next_list() is
>     similar, but can't fail (see commit since 08f9541).
> 
> Can touch up on commit.
> 

Yes, sounds better.


>> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>>                             GenericAlternate **obj, size_t size,
>>                             bool promote_int, Error **errp)
>>  {
>> +    Error *err = NULL;
>> +
>>      assert(obj && size >= sizeof(GenericAlternate));
>>      if (v->start_alternate) {
>> -        v->start_alternate(v, name, obj, size, promote_int, errp);
>> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>>      }
>> +    if (v->type == VISITOR_INPUT) {
>> +        assert(!err != !*obj);
> 
> Could assert(v->start_alternate && !err != !*obj), to preempt "what if
> !v->start_alternate" worries.  If you like that, I can do it on commit.

Can't hurt :)

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

* Re: [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced
  2016-04-29  8:50   ` Markus Armbruster
@ 2016-04-29 12:15     ` Eric Blake
  2016-04-29 13:03       ` Markus Armbruster
  0 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-29 12:15 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 04/29/2016 02:50 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> In the QMP input visitor, visiting a list traverses two objects:
>> the QAPI GenericList of the caller (which gets advanced in
>> visit_next_list() regardless of this patch), and the QList input
>> that we are converting to QAPI.  For consistency with QDict
>> visits, we want to consume elements from the input QList during
>> the visit_type_FOO() for the list element; that is, we want ALL
>> the code for consuming an input to live in qmp_input_get_object(),
>> rather than having it split according to whether we are visiting
>> a dict or a list.  Making qmp_input_get_object() the common point
>> of consumption will make it easier for a later patch to refactor
>> visit_start_list() to cover the GenericList * head of a QAPI list,
>> and in turn will get rid of the 'first' flag (which lived in
>> qmp_input_next_list() pre-patch, and is hoisted to StackObject
>> by this patch).
>>
>> This patch is therefore shifting the post-condition use of
>> 'entry' from:
>>
>>         start_list next_list type_ELT ... next_list type_ELT next_list end_list
>>  visit                       1st elt                last elt
>>  entry  NULL       1st elt   1st elt      last elt  last elt NULL      gone
> 
> You added a line "visit", but the introduction still only mentions
> 'entry'.  Mildly confusing.  If you have an idea on how to improve it, I
> can touch it up on commit.

[probably word wrapped by my mailer, but how about]

This patch is therefore altering the post-condition use of 'entry',
while keeping what gets visited unchanged, from:

         start_list next_list type_ELT ... next_list type_ELT next_list
end_list
  visits                      1st elt                last elt
  entry  NULL       1st elt   1st elt      last elt  last elt NULL      gone


> 
>>
>> where type_ELT() returns (entry ? entry : 1st elt) and next_list() steps
>> entry
>>
>> to this usage:
>>
>>         start_list next_list type_ELT ... next_list type_ELT next_list end_list
>>  visit                       1st elt                last elt

and another s/visit/visits/ here

>>  entry  1st elt    1nd elt   2nd elt      last elt  NULL     NULL      gone
>>
>> where type_ELT() steps entry and returns the old entry, and next_list()
>> leaves entry alone.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> Patch looks good.

I added the visit line to make it a little easier to see that type_ELT
is still visiting the same thing, even though 'entry' ends up on a
different spot (easier than deciphering "type_ELT() ... returns the old
entry").  But if you don't think it adds anything, I'm also okay with
dropping the 'visit[s]' line and sticking to just the phrase after the
diagram for that explanation.

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

* Re: [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-04-29 11:13 ` [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster
@ 2016-04-29 12:16   ` Eric Blake
  2016-04-29 13:09     ` Markus Armbruster
  0 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-29 12:16 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

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

On 04/29/2016 05:13 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> 2.7 material; much less churn this time through, but enough
>> that it was easier for me to repost than to make Markus do
>> the touchups his review found.
>>
>> Based on master, with no prerequisite patches.
>>
>> Also available as a tag at this location:
>> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv16e
>>
>> 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
>>
>> v15 was:
>> https://lists.gnu.org/archive/html/qemu-devel/2016-04/msg04147.html
>>
>> Since then, I deferred the qmp-commands.hx patch to later
>> (Marc-André's solution may be better), and split two others
>> while addressing review comments.
> 
> Looks ready.  Thanks for the quick respin!

As usual, I'll be glad double-check your qapi-next branch once you've
made the tweaks you suggested while applying the series.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

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

* Re: [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error
  2016-04-29 12:10     ` Eric Blake
@ 2016-04-29 12:17       ` Eric Blake
  2016-04-29 12:59         ` Markus Armbruster
  0 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-29 12:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 04/29/2016 06:10 AM, Eric Blake wrote:
> On 04/29/2016 02:28 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> Our existing input visitors were not very consistent on errors
>>> in a function taking 'TYPE **obj' (that is, start_struct(),
>>> start_alternate(), type_str(), and type_any(). next_list() is
>>> similar, except that since commit 08f9541, it can't fail).
>>
>> Multiple sentences in a parenthesis, ugh :)
>>
>>     Our existing input visitors were not very consistent on errors in a
>>     function taking 'TYPE **obj'.  These are start_struct(),
>>     start_alternate(), type_str(), and type_any().  next_list() is
>>     similar, but can't fail (see commit since 08f9541).
>>
>> Can touch up on commit.
>>
> 
> Yes, sounds better.

Spoke too soon: "see commit since 08f9541" is too wordy, should be
either "see commit 08f9541" or "since commit 08f9541"

> 
> 
>>> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>>>                             GenericAlternate **obj, size_t size,
>>>                             bool promote_int, Error **errp)
>>>  {
>>> +    Error *err = NULL;
>>> +
>>>      assert(obj && size >= sizeof(GenericAlternate));
>>>      if (v->start_alternate) {
>>> -        v->start_alternate(v, name, obj, size, promote_int, errp);
>>> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>>>      }
>>> +    if (v->type == VISITOR_INPUT) {
>>> +        assert(!err != !*obj);
>>
>> Could assert(v->start_alternate && !err != !*obj), to preempt "what if
>> !v->start_alternate" worries.  If you like that, I can do it on commit.
> 
> Can't hurt :)
> 

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

* Re: [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error
  2016-04-29 12:17       ` Eric Blake
@ 2016-04-29 12:59         ` Markus Armbruster
  0 siblings, 0 replies; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29 12:59 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 04/29/2016 06:10 AM, Eric Blake wrote:
>> On 04/29/2016 02:28 AM, Markus Armbruster wrote:
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> Our existing input visitors were not very consistent on errors
>>>> in a function taking 'TYPE **obj' (that is, start_struct(),
>>>> start_alternate(), type_str(), and type_any(). next_list() is
>>>> similar, except that since commit 08f9541, it can't fail).
>>>
>>> Multiple sentences in a parenthesis, ugh :)
>>>
>>>     Our existing input visitors were not very consistent on errors in a
>>>     function taking 'TYPE **obj'.  These are start_struct(),
>>>     start_alternate(), type_str(), and type_any().  next_list() is
>>>     similar, but can't fail (see commit since 08f9541).
>>>
>>> Can touch up on commit.
>>>
>> 
>> Yes, sounds better.
>
> Spoke too soon: "see commit since 08f9541" is too wordy, should be
> either "see commit 08f9541" or "since commit 08f9541"

I'm terribly prone to leaving unwanted words behind when tinkering with
a sentence...  Thanks!

>>>> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>>>>                             GenericAlternate **obj, size_t size,
>>>>                             bool promote_int, Error **errp)
>>>>  {
>>>> +    Error *err = NULL;
>>>> +
>>>>      assert(obj && size >= sizeof(GenericAlternate));
>>>>      if (v->start_alternate) {
>>>> -        v->start_alternate(v, name, obj, size, promote_int, errp);
>>>> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>>>>      }
>>>> +    if (v->type == VISITOR_INPUT) {
>>>> +        assert(!err != !*obj);
>>>
>>> Could assert(v->start_alternate && !err != !*obj), to preempt "what if
>>> !v->start_alternate" worries.  If you like that, I can do it on commit.
>> 
>> Can't hurt :)

Done.

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

* Re: [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced
  2016-04-29 12:15     ` Eric Blake
@ 2016-04-29 13:03       ` Markus Armbruster
  0 siblings, 0 replies; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29 13:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 04/29/2016 02:50 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> In the QMP input visitor, visiting a list traverses two objects:
>>> the QAPI GenericList of the caller (which gets advanced in
>>> visit_next_list() regardless of this patch), and the QList input
>>> that we are converting to QAPI.  For consistency with QDict
>>> visits, we want to consume elements from the input QList during
>>> the visit_type_FOO() for the list element; that is, we want ALL
>>> the code for consuming an input to live in qmp_input_get_object(),
>>> rather than having it split according to whether we are visiting
>>> a dict or a list.  Making qmp_input_get_object() the common point
>>> of consumption will make it easier for a later patch to refactor
>>> visit_start_list() to cover the GenericList * head of a QAPI list,
>>> and in turn will get rid of the 'first' flag (which lived in
>>> qmp_input_next_list() pre-patch, and is hoisted to StackObject
>>> by this patch).
>>>
>>> This patch is therefore shifting the post-condition use of
>>> 'entry' from:
>>>
>>>         start_list next_list type_ELT ... next_list type_ELT next_list end_list
>>>  visit                       1st elt                last elt
>>>  entry  NULL       1st elt   1st elt      last elt  last elt NULL      gone
>> 
>> You added a line "visit", but the introduction still only mentions
>> 'entry'.  Mildly confusing.  If you have an idea on how to improve it, I
>> can touch it up on commit.
>
> [probably word wrapped by my mailer, but how about]
>
> This patch is therefore altering the post-condition use of 'entry',
> while keeping what gets visited unchanged, from:
>
>          start_list next_list type_ELT ... next_list type_ELT next_list
> end_list
>   visits                      1st elt                last elt
>   entry  NULL       1st elt   1st elt      last elt  last elt NULL      gone

Sold.

>>>
>>> where type_ELT() returns (entry ? entry : 1st elt) and next_list() steps
>>> entry
>>>
>>> to this usage:
>>>
>>>         start_list next_list type_ELT ... next_list type_ELT next_list end_list
>>>  visit                       1st elt                last elt
>
> and another s/visit/visits/ here
>
>>>  entry  1st elt    1nd elt   2nd elt      last elt  NULL     NULL      gone
>>>
>>> where type_ELT() steps entry and returns the old entry, and next_list()
>>> leaves entry alone.
>>>
>>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> 
>> Patch looks good.
>
> I added the visit line to make it a little easier to see that type_ELT
> is still visiting the same thing, even though 'entry' ends up on a
> different spot (easier than deciphering "type_ELT() ... returns the old
> entry").  But if you don't think it adds anything, I'm also okay with
> dropping the 'visit[s]' line and sticking to just the phrase after the
> diagram for that explanation.

Either way works for me, so I'm sticking to your later version, as
amended here.

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

* Re: [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-04-29 12:16   ` Eric Blake
@ 2016-04-29 13:09     ` Markus Armbruster
  2016-04-29 14:09       ` Eric Blake
  0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2016-04-29 13:09 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 04/29/2016 05:13 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> 2.7 material; much less churn this time through, but enough
>>> that it was easier for me to repost than to make Markus do
>>> the touchups his review found.
>>>
>>> Based on master, with no prerequisite patches.
>>>
>>> Also available as a tag at this location:
>>> git fetch git://repo.or.cz/qemu/ericb.git qapi-cleanupv16e
>>>
>>> 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
>>>
>>> v15 was:
>>> https://lists.gnu.org/archive/html/qemu-devel/2016-04/msg04147.html
>>>
>>> Since then, I deferred the qmp-commands.hx patch to later
>>> (Marc-André's solution may be better), and split two others
>>> while addressing review comments.
>> 
>> Looks ready.  Thanks for the quick respin!
>
> As usual, I'll be glad double-check your qapi-next branch once you've
> made the tweaks you suggested while applying the series.

Applied to qapi-next, thanks!

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

* Re: [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-04-29 13:09     ` Markus Armbruster
@ 2016-04-29 14:09       ` Eric Blake
  2016-05-04 13:54         ` Markus Armbruster
  0 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-04-29 14:09 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

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

On 04/29/2016 07:09 AM, Markus Armbruster wrote:

>>> Looks ready.  Thanks for the quick respin!
>>
>> As usual, I'll be glad double-check your qapi-next branch once you've
>> made the tweaks you suggested while applying the series.
> 
> Applied to qapi-next, thanks!

Looks good; I'll start basing my other patches on this branch until 2.7
opens.

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

* Re: [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-04-29 14:09       ` Eric Blake
@ 2016-05-04 13:54         ` Markus Armbruster
  2016-05-04 14:07           ` Eric Blake
  0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2016-05-04 13:54 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Uh, in visitor.h at the end of this series:

 * <example>
 *  Visitor *v;
 *  Error *err = NULL;
 *  int value;
 *
 *  v = ...obtain visitor...
 *  visit_start_struct(v, NULL, NULL, 0, &err);
 *  if (err) {
--->    goto outobj;
 *  }
 *  visit_start_list(v, "list", NULL, 0, &err);
 *  if (err) {
 *      goto outobj;
 *  }
 *  value = 1;
 *  visit_type_int(v, NULL, &value, &err);
 *  if (err) {
 *      goto outlist;
 *  }
 *  value = 2;
 *  visit_type_int(v, NULL, &value, &err);
 *  if (err) {
 *      goto outlist;
 *  }
 * outlist:
 *  visit_end_list(v);
 *  if (!err) {
 *      visit_check_struct(v, &err);
 *  }
 * outobj:
 *  visit_end_struct(v);
 *  error_propagate(errp, err);
 *  ...clean up v...
 * </example>

The ---> goto is wrong; we call visit_end_struct() after
visit_start_struct() failed.  Haven't dug through the patches to find
the guilty one.

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

* [Qemu-devel] [PATCH] fixup! qapi: Document visitor interfaces, add assertions
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 12/24] qapi: Document visitor interfaces, add assertions Eric Blake
@ 2016-05-04 14:05   ` Eric Blake
  2016-05-04 14:49     ` Eric Blake
  2016-05-04 15:04     ` Markus Armbruster
  0 siblings, 2 replies; 44+ messages in thread
From: Eric Blake @ 2016-05-04 14:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

[no change to commit message]

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 include/qapi/visitor.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 0bb2fe1..2211637 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -174,7 +174,7 @@
  *  v = ...obtain visitor...
  *  visit_start_struct(v, NULL, NULL, 0, &err);
  *  if (err) {
- *      goto outobj;
+ *      goto out;
  *  }
  *  visit_start_list(v, "list", &err);
  *  if (err) {
@@ -196,6 +196,7 @@
  *  error_propagate(errp, err);
  *  err = NULL;
  *  visit_end_struct(v, &err);
+ * out:
  *  error_propagate(errp, err);
  *  ...clean up v...
  * </example>
-- 
2.5.5

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

* Re: [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E)
  2016-05-04 13:54         ` Markus Armbruster
@ 2016-05-04 14:07           ` Eric Blake
  0 siblings, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-05-04 14:07 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

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

On 05/04/2016 07:54 AM, Markus Armbruster wrote:
> Uh, in visitor.h at the end of this series:
> 
>  * <example>
>  *  Visitor *v;
>  *  Error *err = NULL;
>  *  int value;
>  *
>  *  v = ...obtain visitor...
>  *  visit_start_struct(v, NULL, NULL, 0, &err);
>  *  if (err) {
> --->    goto outobj;

>  * outobj:
>  *  visit_end_struct(v);
>  *  error_propagate(errp, err);
>  *  ...clean up v...
>  * </example>
> 
> The ---> goto is wrong; we call visit_end_struct() after
> visit_start_struct() failed.  Haven't dug through the patches to find
> the guilty one.
> 

Fairly easy fix, and only affects doc (patch 12/24 introduced it). I've
sent a fixup that you can squash in, since qapi-next is not merged to
master yet.

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

* Re: [Qemu-devel] [PATCH] fixup! qapi: Document visitor interfaces, add assertions
  2016-05-04 14:05   ` [Qemu-devel] [PATCH] fixup! " Eric Blake
@ 2016-05-04 14:49     ` Eric Blake
  2016-05-04 15:04     ` Markus Armbruster
  1 sibling, 0 replies; 44+ messages in thread
From: Eric Blake @ 2016-05-04 14:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

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

On 05/04/2016 08:05 AM, Eric Blake wrote:
> [no change to commit message]
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>  include/qapi/visitor.h | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 0bb2fe1..2211637 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -174,7 +174,7 @@
>   *  v = ...obtain visitor...
>   *  visit_start_struct(v, NULL, NULL, 0, &err);
>   *  if (err) {
> - *      goto outobj;
> + *      goto out;
>   *  }
>   *  visit_start_list(v, "list", &err);
>   *  if (err) {
> @@ -196,6 +196,7 @@
>   *  error_propagate(errp, err);
>   *  err = NULL;
>   *  visit_end_struct(v, &err);
> + * out:

This also conflicts with 19/24; resolution is pretty obvious (keep the
new out: label after the updated visit_end_struct() call).

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

* Re: [Qemu-devel] [PATCH] fixup! qapi: Document visitor interfaces, add assertions
  2016-05-04 14:05   ` [Qemu-devel] [PATCH] fixup! " Eric Blake
  2016-05-04 14:49     ` Eric Blake
@ 2016-05-04 15:04     ` Markus Armbruster
  1 sibling, 0 replies; 44+ messages in thread
From: Markus Armbruster @ 2016-05-04 15:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> [no change to commit message]
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>  include/qapi/visitor.h | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 0bb2fe1..2211637 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -174,7 +174,7 @@
>   *  v = ...obtain visitor...
>   *  visit_start_struct(v, NULL, NULL, 0, &err);
>   *  if (err) {
> - *      goto outobj;
> + *      goto out;
>   *  }
>   *  visit_start_list(v, "list", &err);
>   *  if (err) {
> @@ -196,6 +196,7 @@
>   *  error_propagate(errp, err);
>   *  err = NULL;
>   *  visit_end_struct(v, &err);
> + * out:
>   *  error_propagate(errp, err);
>   *  ...clean up v...
>   * </example>

Squashed in and pushed.  Thanks!

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

* [Qemu-devel] [PATCH v16A 17/24] qmp: Don't reuse qmp visitor after grabbing output
  2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 17/24] qmp: Add qmp_output_visitor_reset() Eric Blake
@ 2016-05-10  4:20   ` Eric Blake
  2016-05-10  8:18     ` Markus Armbruster
  0 siblings, 1 reply; 44+ messages in thread
From: Eric Blake @ 2016-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

The testsuite was the only client that attempted to reuse a
QmpOutputVisitor for a second visit after encountering an
error and/or calling qmp_output_get_qobject() on a first
visit.  The next patch is about to tighten the semantics to
be one-shot usage of the visitor, like all other visitors
(which will enable further simplifications down the road).

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

This commit is designed to completely replace commit 51750617
on Markus' qapi-next branch; all other patches in that series
remain unchanged and the testsuite still passes.  Doing this
now will make it easier for my next series to introduce a
generalized visit_free(Visitor*) instead of our current
duplication of a per-subtype foo_visit_cleanup(FooVisitor*),
without having to revert the addition of qmp_output_visitor_reset().

 tests/test-qmp-output-visitor.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 8acc229..5543511 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -43,6 +43,12 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
     data->ov = NULL;
 }

+static void visitor_reset(TestOutputVisitorData *data)
+{
+    visitor_output_teardown(data, NULL);
+    visitor_output_setup(data, NULL);
+}
+
 static void test_visitor_out_int(TestOutputVisitorData *data,
                                  const void *unused)
 {
@@ -139,6 +145,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
         g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
                         EnumOne_lookup[i]);
         qobject_decref(obj);
+        visitor_reset(data);
     }
 }

@@ -153,6 +160,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
         visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
         g_assert(err);
         error_free(err);
+        visitor_reset(data);
     }
 }

@@ -262,6 +270,7 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
         visit_type_UserDefOne(data->ov, "unused", &pu, &err);
         g_assert(err);
         error_free(err);
+        visitor_reset(data);
     }
 }

@@ -366,6 +375,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qobject_decref(obj);
     qobject_decref(qobj);

+    visitor_reset(data);
     qdict = qdict_new();
     qdict_put(qdict, "integer", qint_from_int(-42));
     qdict_put(qdict, "boolean", qbool_from_bool(true));
@@ -442,6 +452,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     qapi_free_UserDefAlternate(tmp);
     qobject_decref(arg);

+    visitor_reset(data);
     tmp = g_new0(UserDefAlternate, 1);
     tmp->type = QTYPE_QSTRING;
     tmp->u.s = g_strdup("hello");
@@ -455,6 +466,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     qapi_free_UserDefAlternate(tmp);
     qobject_decref(arg);

+    visitor_reset(data);
     tmp = g_new0(UserDefAlternate, 1);
     tmp->type = QTYPE_QDICT;
     tmp->u.udfu.integer = 1;
-- 
2.5.5

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

* Re: [Qemu-devel] [PATCH v16A 17/24] qmp: Don't reuse qmp visitor after grabbing output
  2016-05-10  4:20   ` [Qemu-devel] [PATCH v16A 17/24] qmp: Don't reuse qmp visitor after grabbing output Eric Blake
@ 2016-05-10  8:18     ` Markus Armbruster
  0 siblings, 0 replies; 44+ messages in thread
From: Markus Armbruster @ 2016-05-10  8:18 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> The testsuite was the only client that attempted to reuse a
> QmpOutputVisitor for a second visit after encountering an
> error and/or calling qmp_output_get_qobject() on a first
> visit.  The next patch is about to tighten the semantics to
> be one-shot usage of the visitor, like all other visitors
> (which will enable further simplifications down the road).
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>
> This commit is designed to completely replace commit 51750617
> on Markus' qapi-next branch; all other patches in that series
> remain unchanged and the testsuite still passes.  Doing this
> now will make it easier for my next series to introduce a
> generalized visit_free(Visitor*) instead of our current
> duplication of a per-subtype foo_visit_cleanup(FooVisitor*),
> without having to revert the addition of qmp_output_visitor_reset().

Patch looks good to me.  I massaged qapi-next as instructed.  Thanks!

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

end of thread, other threads:[~2016-05-10  8:18 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-28 21:45 [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 01/24] qapi-visit: Add visitor.type classification Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 02/24] qapi: Guarantee NULL obj on input visitor callback error Eric Blake
2016-04-29  8:28   ` Markus Armbruster
2016-04-29 12:10     ` Eric Blake
2016-04-29 12:17       ` Eric Blake
2016-04-29 12:59         ` Markus Armbruster
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 03/24] qmp: Drop dead command->type Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 04/24] qmp-input: Clean up stack handling Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 05/24] qapi: Consolidate QMP input visitor creation Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 06/24] qapi: Use strict QMP input visitor in more places Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 07/24] qmp-input: Don't consume input when checking has_member Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 08/24] qapi-commands: Wrap argument visit in visit_start_struct Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 09/24] qom: Wrap prop " Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 10/24] qmp-input: Require struct push to visit members of top dict Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 11/24] qmp-input: Refactor when list is advanced Eric Blake
2016-04-29  8:50   ` Markus Armbruster
2016-04-29 12:15     ` Eric Blake
2016-04-29 13:03       ` Markus Armbruster
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 12/24] qapi: Document visitor interfaces, add assertions Eric Blake
2016-05-04 14:05   ` [Qemu-devel] [PATCH] fixup! " Eric Blake
2016-05-04 14:49     ` Eric Blake
2016-05-04 15:04     ` Markus Armbruster
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 13/24] tests: Add check-qnull Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 14/24] qapi: Add visit_type_null() visitor Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 15/24] qmp: Support explicit null during visits Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 16/24] spapr_drc: Expose 'null' in qom-get when there is no fdt Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 17/24] qmp: Add qmp_output_visitor_reset() Eric Blake
2016-05-10  4:20   ` [Qemu-devel] [PATCH v16A 17/24] qmp: Don't reuse qmp visitor after grabbing output Eric Blake
2016-05-10  8:18     ` Markus Armbruster
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 18/24] qmp: Tighten output visitor rules Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 19/24] qapi: Split visit_end_struct() into pieces Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 20/24] qapi: Don't pass NULL to printf in string input visitor Eric Blake
2016-04-29  9:03   ` Markus Armbruster
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 21/24] tests/string-input-visitor: Add negative integer tests Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 22/24] qapi: Fix string input visitor handling of invalid list Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 23/24] qapi: Simplify semantics of visit_next_list() Eric Blake
2016-04-28 21:45 ` [Qemu-devel] [PATCH v16 24/24] qapi: Change visit_type_FOO() to no longer return partial objects Eric Blake
2016-04-29 11:13 ` [Qemu-devel] [PATCH v16 00/24] qapi visitor cleanups (post-introspection cleanups subset E) Markus Armbruster
2016-04-29 12:16   ` Eric Blake
2016-04-29 13:09     ` Markus Armbruster
2016-04-29 14:09       ` Eric Blake
2016-05-04 13:54         ` Markus Armbruster
2016-05-04 14:07           ` Eric Blake

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.