All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors
@ 2016-05-19  4:40 Eric Blake
  2016-05-19  4:40   ` [Qemu-devel] " Eric Blake
                   ` (31 more replies)
  0 siblings, 32 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

Hard prerequisites (for all patches to apply):
Markus' qjson movement:
https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg00471.html

Soft prerequisites (for valgrind to be happy with all touched tests):
My fix for memleak in range.h:
https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg02666.html
My fix for memleak in json-streamer.c:
https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg03186.html

Since v3:
Address review comments (lots from Markus, a few more from others)
Drop patches 8-11/18 (qjson.h remains a one-off for migration)
Drop patch 12/18 (done via nicer role reversal in 26/28)
Defer Infinity/NaN rejection to later
Rearrange lots of patches; the clone visitor half (1-15) is simpler
than the JSON visitor (16-28), if we need to subdivide the series
Lots of new patches, in part implementing cleanups that make
later patches simpler

The churn between revisions is pretty daunting; so I suspect
we'll be needing a v5 as a result.  But I'm really happy
with how the role reversal of qobject_to_json() turned out,
as well as how the polymorphic visit_complete() and
visit_free() allowed me to hide knowledge of visitor subtypes
from the rest of the tree.  And working on this series let
me plug a couple of other bugs

001/28:[----] [--] 'qapi: Rename (one) qjson.h to qobject-json.h'
002/28:[----] [--] 'qapi: Improve use of qmp/types.h'
003/28:[down] 'qemu-img: Don't leak errors when outputting JSON'
004/28:[0043] [FC] 'qapi: Add parameter to visit_end_*'
005/28:[down] 'qapi: Add new visit_free() function'
006/28:[down] 'opts-visitor: Favor new visit_free() function'
007/28:[down] 'string-input-visitor: Favor new visit_free() function'
008/28:[down] 'qmp-input-visitor: Favor new visit_free() function'
009/28:[down] 'string-output-visitor: Favor new visit_free() function'
010/28:[down] 'qmp-output-visitor: Favor new visit_free() function'
011/28:[down] 'tests: Factor out common code in qapi output tests'
012/28:[down] 'qapi: Add new visit_complete() function'
013/28:[0312] [FC] 'qapi: Add new clone visitor'
014/28:[0043] [FC] 'sockets: Use new QAPI cloning'
015/28:[0024] [FC] 'replay: Use new QAPI cloning'
016/28:[0028] [FC] 'qapi: Factor out JSON string escaping'
017/28:[0033] [FC] 'qapi: Factor out JSON number formatting'
018/28:[down] 'qapi: Add qstring_append_printf()'
019/28:[0030] [FC] 'qapi: Use qstring_append_chr() where appropriate'
020/28:[down] 'qstring: Add qstring_consume_str()'
021/28:[down] 'qstring: Add qstring_wrap_str()'
022/28:[down] 'qobject: Consolidate qobject_to_json() calls'
023/28:[down] 'tests: Test qobject_to_json() pretty formatting'
024/28:[down] 'qapi: Add JSON output visitor'
025/28:[0081] [FC] 'qapi: Support pretty printing in JSON output visitor'
026/28:[down] 'qobject: Implement qobject_to_json() atop JSON visitor'
027/28:[down] 'qapi: Add 'any' support to JSON output'
028/28:[0068] [FC] 'qemu-img: Use new JSON output formatter'

Eric Blake (28):
  qapi: Rename (one) qjson.h to qobject-json.h
  qapi: Improve use of qmp/types.h
  qemu-img: Don't leak errors when outputting JSON
  qapi: Add parameter to visit_end_*
  qapi: Add new visit_free() function
  opts-visitor: Favor new visit_free() function
  string-input-visitor: Favor new visit_free() function
  qmp-input-visitor: Favor new visit_free() function
  string-output-visitor: Favor new visit_free() function
  qmp-output-visitor: Favor new visit_free() function
  tests: Factor out common code in qapi output tests
  qapi: Add new visit_complete() function
  qapi: Add new clone visitor
  sockets: Use new QAPI cloning
  replay: Use new QAPI cloning
  qapi: Factor out JSON string escaping
  qapi: Factor out JSON number formatting
  qapi: Add qstring_append_printf()
  qapi: Use qstring_append_chr() where appropriate
  qstring: Add qstring_consume_str()
  qstring: Add qstring_wrap_str()
  qobject: Consolidate qobject_to_json() calls
  tests: Test qobject_to_json() pretty formatting
  qapi: Add JSON output visitor
  qapi: Support pretty printing in JSON output visitor
  qobject: Implement qobject_to_json() atop JSON visitor
  qapi: Add 'any' support to JSON output
  qemu-img: Use new JSON output formatter

 include/qapi/visitor.h                        | 147 +++++---
 include/qapi/visitor-impl.h                   |  24 +-
 scripts/qapi-commands.py                      |  33 +-
 scripts/qapi-event.py                         |  12 +-
 scripts/qapi-types.py                         |   6 +-
 scripts/qapi-visit.py                         |   8 +-
 include/io/task.h                             |   2 +-
 include/qapi/clone-visitor.h                  |  39 +++
 include/qapi/dealloc-visitor.h                |   5 +-
 include/qapi/json-output-visitor.h            |  29 ++
 include/qapi/opts-visitor.h                   |   4 +-
 include/qapi/qmp-input-visitor.h              |   6 +-
 include/qapi/qmp-output-visitor.h             |  12 +-
 include/qapi/qmp/{qjson.h => qobject-json.h}  |  16 +-
 include/qapi/qmp/qstring.h                    |   9 +-
 include/qapi/qmp/types.h                      |   1 -
 include/qapi/string-input-visitor.h           |   5 +-
 include/qapi/string-output-visitor.h          |  11 +-
 include/qemu/sockets.h                        |   4 -
 qapi/qapi-visit-core.c                        |  47 ++-
 balloon.c                                     |   2 +-
 block.c                                       |   7 +-
 block/archipelago.c                           |   8 +-
 block/crypto.c                                |  30 +-
 block/nbd.c                                   |   2 +-
 block/qapi.c                                  |   9 +-
 block/quorum.c                                |   2 +-
 blockdev.c                                    |  12 +-
 blockjob.c                                    |   2 +-
 hmp.c                                         |  19 +-
 hw/acpi/core.c                                |   8 +-
 hw/core/qdev.c                                |   2 +-
 hw/misc/pvpanic.c                             |   2 +-
 hw/net/virtio-net.c                           |   2 +-
 hw/pci/pcie_aer.c                             |   1 +
 hw/ppc/spapr_drc.c                            |   4 +-
 hw/virtio/virtio-balloon.c                    |   4 +-
 io/channel-socket.c                           |   9 +-
 migration/qjson.c                             |  12 +-
 monitor.c                                     |  18 +-
 net/net.c                                     |  16 +-
 numa.c                                        |   6 +-
 qapi/json-output-visitor.c                    | 211 ++++++++++++
 qapi/opts-visitor.c                           |  37 +-
 qapi/qapi-clone-visitor.c                     | 178 ++++++++++
 qapi/qapi-dealloc-visitor.c                   |  59 +---
 qapi/qmp-dispatch.c                           |   1 +
 qapi/qmp-event.c                              |   2 +-
 qapi/qmp-input-visitor.c                      |  18 +-
 qapi/qmp-output-visitor.c                     |  37 +-
 qapi/string-input-visitor.c                   |  20 +-
 qapi/string-output-visitor.c                  |  29 +-
 qemu-char.c                                   |   5 +-
 qemu-img.c                                    |  66 ++--
 qga/main.c                                    |   7 +-
 qmp.c                                         |   9 +-
 qobject/json-parser.c                         |  32 +-
 qobject/qjson.c                               | 296 ----------------
 qobject/qobject-json.c                        | 245 +++++++++++++
 qobject/qobject.c                             |   7 +-
 qobject/qstring.c                             |  74 +++-
 qom/object.c                                  |  61 ++--
 qom/object_interfaces.c                       |  12 +-
 qom/qom-qobject.c                             |  19 +-
 replay/replay-input.c                         |  34 +-
 target-s390x/kvm.c                            |   2 +-
 tests/check-qnull.c                           |  17 +-
 tests/{check-qjson.c => check-qobject-json.c} | 114 +++++--
 tests/check-qstring.c                         |  55 ++-
 tests/libqtest.c                              |   4 +-
 tests/test-clone-visitor.c                    | 207 +++++++++++
 tests/test-json-output-visitor.c              | 475 ++++++++++++++++++++++++++
 tests/test-opts-visitor.c                     |   9 +-
 tests/test-qmp-commands.c                     |   8 +-
 tests/test-qmp-input-strict.c                 |  13 +-
 tests/test-qmp-input-visitor.c                |  15 +-
 tests/test-qmp-output-visitor.c               |  97 ++----
 tests/test-string-input-visitor.c             |  22 +-
 tests/test-string-output-visitor.c            |  99 +++---
 tests/test-visitor-serialization.c            |  43 +--
 ui/spice-core.c                               |   2 +-
 util/qemu-sockets.c                           |  27 --
 vl.c                                          |   2 +-
 MAINTAINERS                                   |   2 +-
 docs/qapi-code-gen.txt                        |  39 +--
 qapi/Makefile.objs                            |   2 +-
 qobject/Makefile.objs                         |   3 +-
 tests/.gitignore                              |   4 +-
 tests/Makefile                                |  15 +-
 tests/qemu-iotests/043.out                    |  22 +-
 90 files changed, 2237 insertions(+), 1117 deletions(-)
 create mode 100644 include/qapi/clone-visitor.h
 create mode 100644 include/qapi/json-output-visitor.h
 rename include/qapi/qmp/{qjson.h => qobject-json.h} (51%)
 create mode 100644 qapi/json-output-visitor.c
 create mode 100644 qapi/qapi-clone-visitor.c
 delete mode 100644 qobject/qjson.c
 create mode 100644 qobject/qobject-json.c
 rename tests/{check-qjson.c => check-qobject-json.c} (95%)
 create mode 100644 tests/test-clone-visitor.c
 create mode 100644 tests/test-json-output-visitor.c

-- 
2.5.5

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

* [PATCH v4 01/28] qapi: Rename (one) qjson.h to qobject-json.h
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
@ 2016-05-19  4:40   ` Eric Blake
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 02/28] qapi: Improve use of qmp/types.h Eric Blake
                     ` (30 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Chrysostomos Nanakos, Jeff Cody,
	Paolo Bonzini, Alberto Garcia, Michael S. Tsirkin, Jason Wang,
	Luiz Capitulino, Michael Roth, Christian Borntraeger,
	Cornelia Huck, Alexander Graf, Richard Henderson, Gerd Hoffmann,
	open list:Block layer core, open list:Overall

We have two different JSON visitors in the tree; and having both
named 'qjson.h' can cause include confusion.  Rename the qapi
version.

Why did I pick that one?  A later patch plans on deleting the
top-level qjson.c once we have a native JSON output visitor; we
could have renamed that one for less overall churn.  On the other
hand, all of the QObject subtypes have their own qFOO.c file, but
qjson.c makes it sound like we have a QTYPE_JSON subclass of
QObject; the new name of qobject-json makes it obvious that the
file is used for conversions between QObject and JSON, and not a
QObject subtype.

Kill trailing whitespace in the renamed tests/check-qobject-json.c
to keep checkpatch.pl happy.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

---
v4: no change
v3: rebase to master
v2: retitle, enhance commit message, rebase to master
---
 include/qapi/qmp/{qjson.h => qobject-json.h}  |  0
 include/qapi/qmp/types.h                      |  2 +-
 balloon.c                                     |  2 +-
 block.c                                       |  2 +-
 block/archipelago.c                           |  2 +-
 block/nbd.c                                   |  2 +-
 block/quorum.c                                |  2 +-
 blockjob.c                                    |  2 +-
 hw/core/qdev.c                                |  2 +-
 hw/misc/pvpanic.c                             |  2 +-
 hw/net/virtio-net.c                           |  2 +-
 monitor.c                                     |  2 +-
 qapi/qmp-event.c                              |  2 +-
 qemu-img.c                                    |  2 +-
 qga/main.c                                    |  2 +-
 qobject/{qjson.c => qobject-json.c}           |  2 +-
 target-s390x/kvm.c                            |  2 +-
 tests/{check-qjson.c => check-qobject-json.c} | 14 +++++++-------
 tests/libqtest.c                              |  2 +-
 ui/spice-core.c                               |  2 +-
 vl.c                                          |  2 +-
 MAINTAINERS                                   |  2 +-
 qobject/Makefile.objs                         |  3 ++-
 tests/.gitignore                              |  2 +-
 tests/Makefile                                |  8 ++++----
 25 files changed, 34 insertions(+), 33 deletions(-)
 rename include/qapi/qmp/{qjson.h => qobject-json.h} (100%)
 rename qobject/{qjson.c => qobject-json.c} (99%)
 rename tests/{check-qjson.c => check-qobject-json.c} (99%)

diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qobject-json.h
similarity index 100%
rename from include/qapi/qmp/qjson.h
rename to include/qapi/qmp/qobject-json.h
diff --git a/include/qapi/qmp/types.h b/include/qapi/qmp/types.h
index 7782ec5..9109eda 100644
--- a/include/qapi/qmp/types.h
+++ b/include/qapi/qmp/types.h
@@ -20,6 +20,6 @@
 #include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 #endif /* QEMU_OBJECTS_H */
diff --git a/balloon.c b/balloon.c
index f2ef50c..2a11689 100644
--- a/balloon.c
+++ b/balloon.c
@@ -32,7 +32,7 @@
 #include "trace.h"
 #include "qmp-commands.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 static QEMUBalloonEvent *balloon_event_fn;
 static QEMUBalloonStatus *balloon_stat_fn;
diff --git a/block.c b/block.c
index 18a497f..551832f 100644
--- a/block.c
+++ b/block.c
@@ -29,7 +29,7 @@
 #include "qemu/module.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "qemu/notify.h"
diff --git a/block/archipelago.c b/block/archipelago.c
index b9f5e69..104f2f9 100644
--- a/block/archipelago.c
+++ b/block/archipelago.c
@@ -57,7 +57,7 @@
 #include "qemu/thread.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/atomic.h"

 #include <xseg/xseg.h>
diff --git a/block/nbd.c b/block/nbd.c
index 6015e8b..4b357e6 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -33,7 +33,7 @@
 #include "block/block_int.h"
 #include "qemu/module.h"
 #include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qstring.h"
 #include "qemu/cutils.h"
diff --git a/block/quorum.c b/block/quorum.c
index 1ec3511..23954d7 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -20,7 +20,7 @@
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qint.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qstring.h"
 #include "qapi-event.h"
diff --git a/blockjob.c b/blockjob.c
index 9fc37ca..cafa576 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -31,7 +31,7 @@
 #include "block/block_int.h"
 #include "sysemu/block-backend.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/coroutine.h"
 #include "qmp-commands.h"
 #include "qemu/timer.h"
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index db41aa1..1806d8b 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -31,7 +31,7 @@
 #include "sysemu/sysemu.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/visitor.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/error-report.h"
 #include "hw/hotplug.h"
 #include "hw/boards.h"
diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c
index 0ac1e6a..0f2f38e 100644
--- a/hw/misc/pvpanic.c
+++ b/hw/misc/pvpanic.c
@@ -14,7 +14,7 @@

 #include "qemu/osdep.h"
 #include "qapi/qmp/qobject.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "sysemu/sysemu.h"
 #include "qemu/log.h"

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 5798f87..a0ace28 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -22,7 +22,7 @@
 #include "hw/virtio/virtio-net.h"
 #include "net/vhost_net.h"
 #include "hw/virtio/virtio-bus.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi-event.h"
 #include "hw/virtio/virtio-access.h"

diff --git a/monitor.c b/monitor.c
index b6591b9..c1bbc75 100644
--- a/monitor.c
+++ b/monitor.c
@@ -57,7 +57,7 @@
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qbool.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
 #include <qom/object_interfaces.h>
diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c
index 8bba165..af96d61 100644
--- a/qapi/qmp-event.c
+++ b/qapi/qmp-event.c
@@ -16,7 +16,7 @@
 #include "qemu-common.h"
 #include "qapi/qmp-event.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 static QMPEventFuncEmit qmp_emit;

diff --git a/qemu-img.c b/qemu-img.c
index 4792366..b4f93ce 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -26,7 +26,7 @@
 #include "qapi-visit.h"
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/cutils.h"
 #include "qemu/config-file.h"
 #include "qemu/option.h"
diff --git a/qga/main.c b/qga/main.c
index c5527821..1c0315e 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -21,7 +21,7 @@
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/qint.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qga/guest-agent-core.h"
 #include "qemu/module.h"
 #include "qapi/qmp/qerror.h"
diff --git a/qobject/qjson.c b/qobject/qobject-json.c
similarity index 99%
rename from qobject/qjson.c
rename to qobject/qobject-json.c
index ef160d2..bcc3f6e 100644
--- a/qobject/qjson.c
+++ b/qobject/qobject-json.c
@@ -15,7 +15,7 @@
 #include "qapi/qmp/json-lexer.h"
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-streamer.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qbool.h"
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 55ae6d3..6eea6f8 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -36,7 +36,7 @@
 #include "hw/hw.h"
 #include "cpu.h"
 #include "sysemu/device_tree.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "exec/gdbstub.h"
 #include "exec/address-spaces.h"
 #include "trace.h"
diff --git a/tests/check-qjson.c b/tests/check-qobject-json.c
similarity index 99%
rename from tests/check-qjson.c
rename to tests/check-qobject-json.c
index 99de6f5..dc721a8 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qobject-json.c
@@ -19,7 +19,7 @@
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qfloat.h"
 #include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 #include "qemu-common.h"

@@ -64,7 +64,7 @@ static void escaped_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);

@@ -99,7 +99,7 @@ static void simple_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

@@ -107,7 +107,7 @@ static void simple_string(void)
         g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);

         qobject_decref(obj);
-        
+
         QDECREF(str);
     }
 }
@@ -133,7 +133,7 @@ static void single_quote_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

@@ -881,7 +881,7 @@ static void vararg_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

@@ -1145,7 +1145,7 @@ static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
         helper.index = 0;
         helper.objs = lhs->value.qlist;
         helper.result = 1;
-        
+
         qlist_iter(qobject_to_qlist(rhs), compare_helper, &helper);

         return helper.result;
diff --git a/tests/libqtest.c b/tests/libqtest.c
index b12a9e4..2f42bc9 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -24,7 +24,7 @@

 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-streamer.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 #define MAX_IRQ 256
 #define SOCKET_TIMEOUT 5
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 61db3c1..9eec388 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -33,7 +33,7 @@
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qbool.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/notify.h"
 #include "migration/migration.h"
 #include "hw/hw.h"
diff --git a/vl.c b/vl.c
index 5fd22cb..2d62382 100644
--- a/vl.c
+++ b/vl.c
@@ -88,7 +88,7 @@ int main(int argc, char **argv)
 #include "audio/audio.h"
 #include "migration/migration.h"
 #include "sysemu/kvm.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
 #include "qemu-options.h"
diff --git a/MAINTAINERS b/MAINTAINERS
index 81e7fac..c56b657 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1190,7 +1190,7 @@ X: include/qapi/qmp/dispatch.h
 F: tests/check-qdict.c
 F: tests/check-qfloat.c
 F: tests/check-qint.c
-F: tests/check-qjson.c
+F: tests/check-qobject-json.c
 F: tests/check-qlist.c
 F: tests/check-qstring.c
 T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
index bed5508..16a48ec 100644
--- a/qobject/Makefile.objs
+++ b/qobject/Makefile.objs
@@ -1,2 +1,3 @@
 util-obj-y = qnull.o qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
-util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
+util-obj-y += qobject-json.o qobject.o
+util-obj-y += json-lexer.o json-streamer.o json-parser.o
diff --git a/tests/.gitignore b/tests/.gitignore
index a06a8ba..2f8c7ea 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,9 +1,9 @@
 check-qdict
 check-qfloat
 check-qint
-check-qjson
 check-qlist
 check-qnull
+check-qobject-json
 check-qstring
 check-qom-interface
 check-qom-proplist
diff --git a/tests/Makefile b/tests/Makefile
index 1bbd1ca..1c5cb34 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -18,8 +18,8 @@ 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/check-qobject-json$(EXESUF)
+gcov-files-check-qobject-json-y = qobject/qobject-json.c
 check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
 gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
 check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
@@ -385,7 +385,7 @@ GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.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-qnull.o \
-	tests/check-qjson.o \
+	tests/check-qobject-json.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 \
@@ -414,7 +414,7 @@ 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-qobject-json$(EXESUF): tests/check-qobject-json.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)
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
-- 
2.5.5


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

* [Qemu-devel] [PATCH v4 01/28] qapi: Rename (one) qjson.h to qobject-json.h
@ 2016-05-19  4:40   ` Eric Blake
  0 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Chrysostomos Nanakos, Jeff Cody,
	Paolo Bonzini, Alberto Garcia, Michael S. Tsirkin, Jason Wang,
	Luiz Capitulino, Michael Roth, Christian Borntraeger,
	Cornelia Huck, Alexander Graf, Richard Henderson, Gerd Hoffmann,
	open list:Block layer core, open list:Overall

We have two different JSON visitors in the tree; and having both
named 'qjson.h' can cause include confusion.  Rename the qapi
version.

Why did I pick that one?  A later patch plans on deleting the
top-level qjson.c once we have a native JSON output visitor; we
could have renamed that one for less overall churn.  On the other
hand, all of the QObject subtypes have their own qFOO.c file, but
qjson.c makes it sound like we have a QTYPE_JSON subclass of
QObject; the new name of qobject-json makes it obvious that the
file is used for conversions between QObject and JSON, and not a
QObject subtype.

Kill trailing whitespace in the renamed tests/check-qobject-json.c
to keep checkpatch.pl happy.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

---
v4: no change
v3: rebase to master
v2: retitle, enhance commit message, rebase to master
---
 include/qapi/qmp/{qjson.h => qobject-json.h}  |  0
 include/qapi/qmp/types.h                      |  2 +-
 balloon.c                                     |  2 +-
 block.c                                       |  2 +-
 block/archipelago.c                           |  2 +-
 block/nbd.c                                   |  2 +-
 block/quorum.c                                |  2 +-
 blockjob.c                                    |  2 +-
 hw/core/qdev.c                                |  2 +-
 hw/misc/pvpanic.c                             |  2 +-
 hw/net/virtio-net.c                           |  2 +-
 monitor.c                                     |  2 +-
 qapi/qmp-event.c                              |  2 +-
 qemu-img.c                                    |  2 +-
 qga/main.c                                    |  2 +-
 qobject/{qjson.c => qobject-json.c}           |  2 +-
 target-s390x/kvm.c                            |  2 +-
 tests/{check-qjson.c => check-qobject-json.c} | 14 +++++++-------
 tests/libqtest.c                              |  2 +-
 ui/spice-core.c                               |  2 +-
 vl.c                                          |  2 +-
 MAINTAINERS                                   |  2 +-
 qobject/Makefile.objs                         |  3 ++-
 tests/.gitignore                              |  2 +-
 tests/Makefile                                |  8 ++++----
 25 files changed, 34 insertions(+), 33 deletions(-)
 rename include/qapi/qmp/{qjson.h => qobject-json.h} (100%)
 rename qobject/{qjson.c => qobject-json.c} (99%)
 rename tests/{check-qjson.c => check-qobject-json.c} (99%)

diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qobject-json.h
similarity index 100%
rename from include/qapi/qmp/qjson.h
rename to include/qapi/qmp/qobject-json.h
diff --git a/include/qapi/qmp/types.h b/include/qapi/qmp/types.h
index 7782ec5..9109eda 100644
--- a/include/qapi/qmp/types.h
+++ b/include/qapi/qmp/types.h
@@ -20,6 +20,6 @@
 #include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 #endif /* QEMU_OBJECTS_H */
diff --git a/balloon.c b/balloon.c
index f2ef50c..2a11689 100644
--- a/balloon.c
+++ b/balloon.c
@@ -32,7 +32,7 @@
 #include "trace.h"
 #include "qmp-commands.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 static QEMUBalloonEvent *balloon_event_fn;
 static QEMUBalloonStatus *balloon_stat_fn;
diff --git a/block.c b/block.c
index 18a497f..551832f 100644
--- a/block.c
+++ b/block.c
@@ -29,7 +29,7 @@
 #include "qemu/module.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "qemu/notify.h"
diff --git a/block/archipelago.c b/block/archipelago.c
index b9f5e69..104f2f9 100644
--- a/block/archipelago.c
+++ b/block/archipelago.c
@@ -57,7 +57,7 @@
 #include "qemu/thread.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/atomic.h"

 #include <xseg/xseg.h>
diff --git a/block/nbd.c b/block/nbd.c
index 6015e8b..4b357e6 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -33,7 +33,7 @@
 #include "block/block_int.h"
 #include "qemu/module.h"
 #include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qstring.h"
 #include "qemu/cutils.h"
diff --git a/block/quorum.c b/block/quorum.c
index 1ec3511..23954d7 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -20,7 +20,7 @@
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qint.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qstring.h"
 #include "qapi-event.h"
diff --git a/blockjob.c b/blockjob.c
index 9fc37ca..cafa576 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -31,7 +31,7 @@
 #include "block/block_int.h"
 #include "sysemu/block-backend.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/coroutine.h"
 #include "qmp-commands.h"
 #include "qemu/timer.h"
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index db41aa1..1806d8b 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -31,7 +31,7 @@
 #include "sysemu/sysemu.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/visitor.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/error-report.h"
 #include "hw/hotplug.h"
 #include "hw/boards.h"
diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c
index 0ac1e6a..0f2f38e 100644
--- a/hw/misc/pvpanic.c
+++ b/hw/misc/pvpanic.c
@@ -14,7 +14,7 @@

 #include "qemu/osdep.h"
 #include "qapi/qmp/qobject.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "sysemu/sysemu.h"
 #include "qemu/log.h"

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 5798f87..a0ace28 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -22,7 +22,7 @@
 #include "hw/virtio/virtio-net.h"
 #include "net/vhost_net.h"
 #include "hw/virtio/virtio-bus.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi-event.h"
 #include "hw/virtio/virtio-access.h"

diff --git a/monitor.c b/monitor.c
index b6591b9..c1bbc75 100644
--- a/monitor.c
+++ b/monitor.c
@@ -57,7 +57,7 @@
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qbool.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
 #include <qom/object_interfaces.h>
diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c
index 8bba165..af96d61 100644
--- a/qapi/qmp-event.c
+++ b/qapi/qmp-event.c
@@ -16,7 +16,7 @@
 #include "qemu-common.h"
 #include "qapi/qmp-event.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 static QMPEventFuncEmit qmp_emit;

diff --git a/qemu-img.c b/qemu-img.c
index 4792366..b4f93ce 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -26,7 +26,7 @@
 #include "qapi-visit.h"
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/cutils.h"
 #include "qemu/config-file.h"
 #include "qemu/option.h"
diff --git a/qga/main.c b/qga/main.c
index c5527821..1c0315e 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -21,7 +21,7 @@
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/qint.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qga/guest-agent-core.h"
 #include "qemu/module.h"
 #include "qapi/qmp/qerror.h"
diff --git a/qobject/qjson.c b/qobject/qobject-json.c
similarity index 99%
rename from qobject/qjson.c
rename to qobject/qobject-json.c
index ef160d2..bcc3f6e 100644
--- a/qobject/qjson.c
+++ b/qobject/qobject-json.c
@@ -15,7 +15,7 @@
 #include "qapi/qmp/json-lexer.h"
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-streamer.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qbool.h"
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 55ae6d3..6eea6f8 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -36,7 +36,7 @@
 #include "hw/hw.h"
 #include "cpu.h"
 #include "sysemu/device_tree.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "exec/gdbstub.h"
 #include "exec/address-spaces.h"
 #include "trace.h"
diff --git a/tests/check-qjson.c b/tests/check-qobject-json.c
similarity index 99%
rename from tests/check-qjson.c
rename to tests/check-qobject-json.c
index 99de6f5..dc721a8 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qobject-json.c
@@ -19,7 +19,7 @@
 #include "qapi/qmp/qlist.h"
 #include "qapi/qmp/qfloat.h"
 #include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 #include "qemu-common.h"

@@ -64,7 +64,7 @@ static void escaped_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);

@@ -99,7 +99,7 @@ static void simple_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

@@ -107,7 +107,7 @@ static void simple_string(void)
         g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);

         qobject_decref(obj);
-        
+
         QDECREF(str);
     }
 }
@@ -133,7 +133,7 @@ static void single_quote_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

@@ -881,7 +881,7 @@ static void vararg_string(void)

         g_assert(obj != NULL);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
-        
+
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

@@ -1145,7 +1145,7 @@ static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
         helper.index = 0;
         helper.objs = lhs->value.qlist;
         helper.result = 1;
-        
+
         qlist_iter(qobject_to_qlist(rhs), compare_helper, &helper);

         return helper.result;
diff --git a/tests/libqtest.c b/tests/libqtest.c
index b12a9e4..2f42bc9 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -24,7 +24,7 @@

 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-streamer.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 #define MAX_IRQ 256
 #define SOCKET_TIMEOUT 5
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 61db3c1..9eec388 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -33,7 +33,7 @@
 #include "qapi/qmp/qint.h"
 #include "qapi/qmp/qbool.h"
 #include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/notify.h"
 #include "migration/migration.h"
 #include "hw/hw.h"
diff --git a/vl.c b/vl.c
index 5fd22cb..2d62382 100644
--- a/vl.c
+++ b/vl.c
@@ -88,7 +88,7 @@ int main(int argc, char **argv)
 #include "audio/audio.h"
 #include "migration/migration.h"
 #include "sysemu/kvm.h"
-#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
 #include "qemu-options.h"
diff --git a/MAINTAINERS b/MAINTAINERS
index 81e7fac..c56b657 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1190,7 +1190,7 @@ X: include/qapi/qmp/dispatch.h
 F: tests/check-qdict.c
 F: tests/check-qfloat.c
 F: tests/check-qint.c
-F: tests/check-qjson.c
+F: tests/check-qobject-json.c
 F: tests/check-qlist.c
 F: tests/check-qstring.c
 T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
index bed5508..16a48ec 100644
--- a/qobject/Makefile.objs
+++ b/qobject/Makefile.objs
@@ -1,2 +1,3 @@
 util-obj-y = qnull.o qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
-util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
+util-obj-y += qobject-json.o qobject.o
+util-obj-y += json-lexer.o json-streamer.o json-parser.o
diff --git a/tests/.gitignore b/tests/.gitignore
index a06a8ba..2f8c7ea 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,9 +1,9 @@
 check-qdict
 check-qfloat
 check-qint
-check-qjson
 check-qlist
 check-qnull
+check-qobject-json
 check-qstring
 check-qom-interface
 check-qom-proplist
diff --git a/tests/Makefile b/tests/Makefile
index 1bbd1ca..1c5cb34 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -18,8 +18,8 @@ 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/check-qobject-json$(EXESUF)
+gcov-files-check-qobject-json-y = qobject/qobject-json.c
 check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
 gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
 check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
@@ -385,7 +385,7 @@ GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.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-qnull.o \
-	tests/check-qjson.o \
+	tests/check-qobject-json.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 \
@@ -414,7 +414,7 @@ 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-qobject-json$(EXESUF): tests/check-qobject-json.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)
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 02/28] qapi: Improve use of qmp/types.h
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
  2016-05-19  4:40   ` [Qemu-devel] " Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON Eric Blake
                   ` (29 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael S. Tsirkin, Luiz Capitulino, Michael Roth

'qobject-json.h' is not a QObject subtype; include this file
directly in .c files that are using it, rather than abusing
qmp/types.h for that purpose.

Meanwhile, for files that include a list of individual QObject
subtypes, it's easier to just use qmp/types.h for that purpose.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>

---
v4: no change
v3: no change
v2: no change
---
 include/qapi/qmp/types.h           | 1 -
 hw/pci/pcie_aer.c                  | 1 +
 monitor.c                          | 6 +-----
 qapi/qmp-dispatch.c                | 1 +
 qobject/json-parser.c              | 7 +------
 qobject/qobject-json.c             | 6 +-----
 qobject/qobject.c                  | 7 +------
 tests/check-qobject-json.c         | 7 +------
 tests/test-qmp-input-strict.c      | 1 +
 tests/test-qmp-input-visitor.c     | 1 +
 tests/test-qmp-output-visitor.c    | 1 +
 tests/test-visitor-serialization.c | 1 +
 12 files changed, 11 insertions(+), 29 deletions(-)

diff --git a/include/qapi/qmp/types.h b/include/qapi/qmp/types.h
index 9109eda..f21ecf4 100644
--- a/include/qapi/qmp/types.h
+++ b/include/qapi/qmp/types.h
@@ -20,6 +20,5 @@
 #include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qobject-json.h"

 #endif /* QEMU_OBJECTS_H */
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index e2d4e68..63163a6 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -21,6 +21,7 @@
 #include "qemu/osdep.h"
 #include "sysemu/sysemu.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qobject-json.h"
 #include "monitor/monitor.h"
 #include "hw/pci/pci_bridge.h"
 #include "hw/pci/pcie.h"
diff --git a/monitor.c b/monitor.c
index c1bbc75..2f56488 100644
--- a/monitor.c
+++ b/monitor.c
@@ -52,12 +52,8 @@
 #include "qemu/acl.h"
 #include "sysemu/tpm.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qobject-json.h"
+#include "qapi/qmp/types.h"
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/json-parser.h"
 #include <qom/object_interfaces.h>
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 08faf85..1d739cb 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -16,6 +16,7 @@
 #include "qapi/qmp/types.h"
 #include "qapi/qmp/dispatch.h"
 #include "qapi/qmp/json-parser.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi-types.h"
 #include "qapi/qmp/qerror.h"

diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index 67ed727..c18e48a 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -14,12 +14,7 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/types.h"
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-lexer.h"
 #include "qapi/qmp/json-streamer.h"
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index bcc3f6e..24e7d80 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -16,12 +16,8 @@
 #include "qapi/qmp/json-parser.h"
 #include "qapi/qmp/json-streamer.h"
 #include "qapi/qmp/qobject-json.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qdict.h"
 #include "qemu/unicode.h"
+#include "qapi/qmp/types.h"

 typedef struct JSONParsingState
 {
diff --git a/qobject/qobject.c b/qobject/qobject.c
index cd41fb9..fe4fa10 100644
--- a/qobject/qobject.c
+++ b/qobject/qobject.c
@@ -9,12 +9,7 @@

 #include "qemu/osdep.h"
 #include "qemu-common.h"
-#include "qapi/qmp/qbool.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/types.h"

 static void (*qdestroy[QTYPE__MAX])(QObject *) = {
     [QTYPE_NONE] = NULL,               /* No such object exists */
diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
index dc721a8..d889501 100644
--- a/tests/check-qobject-json.c
+++ b/tests/check-qobject-json.c
@@ -13,12 +13,7 @@
 #include "qemu/osdep.h"
 #include <glib.h>

-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qfloat.h"
-#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/types.h"
 #include "qapi/qmp/qobject-json.h"

 #include "qemu-common.h"
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 4602529..edb4b31 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -20,6 +20,7 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qobject-json.h"
 #include "test-qmp-introspect.h"
 #include "qmp-introspect.h"
 #include "qapi-visit.h"
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index cee07ce..d02306c 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -19,6 +19,7 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qobject-json.h"

 typedef struct TestInputVisitorData {
     QObject *obj;
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 1f80e69..9103fef 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -19,6 +19,7 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qobject-json.h"

 typedef struct TestOutputVisitorData {
     QmpOutputVisitor *qov;
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 7b14b5a..d102fa6 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -20,6 +20,7 @@
 #include "test-qapi-visit.h"
 #include "qapi/error.h"
 #include "qapi/qmp/types.h"
+#include "qapi/qmp/qobject-json.h"
 #include "qapi/qmp-input-visitor.h"
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/string-input-visitor.h"
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
  2016-05-19  4:40   ` [Qemu-devel] " Eric Blake
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 02/28] qapi: Improve use of qmp/types.h Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 15:25   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_* Eric Blake
                   ` (28 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Kevin Wolf, Max Reitz, open list:Block layer core

If our JSON output ever encounters an error, we would just silently
leak the local variable.  Instead, assert that our usage won't fail.

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

---
v4: new patch (split out from v3 14/18)
---
 qemu-img.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/qemu-img.c b/qemu-img.c
index b4f93ce..3aa7fb3 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -479,12 +479,11 @@ fail:

 static void dump_json_image_check(ImageCheck *check, bool quiet)
 {
-    Error *local_err = NULL;
     QString *str;
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
     visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check,
-                          &local_err);
+                          &error_abort);
     obj = qmp_output_get_qobject(ov);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
@@ -2170,12 +2169,11 @@ static void dump_snapshots(BlockDriverState *bs)

 static void dump_json_image_info_list(ImageInfoList *list)
 {
-    Error *local_err = NULL;
     QString *str;
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
     visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list,
-                             &local_err);
+                             &error_abort);
     obj = qmp_output_get_qobject(ov);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
@@ -2187,11 +2185,11 @@ static void dump_json_image_info_list(ImageInfoList *list)

 static void dump_json_image_info(ImageInfo *info)
 {
-    Error *local_err = NULL;
     QString *str;
     QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info, &local_err);
+    visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info,
+                         &error_abort);
     obj = qmp_output_get_qobject(ov);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_*
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (2 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 15:36   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function Eric Blake
                   ` (27 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 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

Rather than making the dealloc visitor track of stack of pointers
remembered during visit_start_* in order to free them during
visit_end_*, it's a lot easier to just make all callers pass the
same pointer to visit_end_*.  The generated code has access to the
same pointer, while all other users are doing virtual walks and
can pass NULL.  The dealloc visitor is then greatly simplified.

All three visit_end_*() functions intentionally take a void**,
even though the visit_start_*() functions differ between void**,
GenericList**, and GenericAlternate**.  This is done for several
reasons: when doing a virtual walk, passing NULL doesn't care
what the type is, but when doing a generated walk, we already
have to cast the caller's specific FOO* to call visit_start,
while using void** lets us use visit_end without a cast. Also,
an upcoming patch will add a clone visitor that wants to use
the same implementation for all three visit_end callbacks,
which is made easier if all three share the same signature.

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

---
v4: hoist earlier in series, document why void** is used, update
docs/
v3: new patch
---
 include/qapi/visitor.h          | 32 ++++++++++++++++------------
 include/qapi/visitor-impl.h     |  6 +++---
 scripts/qapi-commands.py        |  4 ++--
 scripts/qapi-event.py           |  2 +-
 scripts/qapi-visit.py           |  8 +++----
 qapi/qapi-visit-core.c          | 12 +++++------
 block/crypto.c                  |  4 ++--
 hw/ppc/spapr_drc.c              |  4 ++--
 hw/virtio/virtio-balloon.c      |  4 ++--
 qapi/opts-visitor.c             |  4 ++--
 qapi/qapi-dealloc-visitor.c     | 47 +++--------------------------------------
 qapi/qmp-input-visitor.c        |  2 +-
 qapi/qmp-output-visitor.c       |  4 ++--
 qapi/string-input-visitor.c     |  2 +-
 qapi/string-output-visitor.c    |  2 +-
 qom/object.c                    |  2 +-
 qom/object_interfaces.c         |  4 ++--
 tests/test-qmp-input-visitor.c  |  2 +-
 tests/test-qmp-output-visitor.c |  2 +-
 docs/qapi-code-gen.txt          |  8 +++----
 20 files changed, 60 insertions(+), 95 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 4d12167..25d0cc2 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -193,12 +193,12 @@
  *      goto outlist;
  *  }
  * outlist:
- *  visit_end_list(v);
+ *  visit_end_list(v, NULL);
  *  if (!err) {
  *      visit_check_struct(v, &err);
  *  }
  * outobj:
- *  visit_end_struct(v);
+ *  visit_end_struct(v, NULL);
  * out:
  *  error_propagate(errp, err);
  *  ...clean up v...
@@ -242,8 +242,8 @@ typedef struct GenericAlternate {
  * 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.
+ * with the same @obj 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?
@@ -267,12 +267,14 @@ void visit_check_struct(Visitor *v, Error **errp);
 /*
  * Complete an object visit started earlier.
  *
+ * @obj must match what was passed to the paired visit_start_struct().
+ *
  * 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);
+void visit_end_struct(Visitor *v, void **obj);


 /*** Visiting lists ***/
@@ -299,8 +301,9 @@ void visit_end_struct(Visitor *v);
  * 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.
+ * element.  Finally, visit_end_list() needs to be called with the
+ * same @list to clean up, even if intermediate visits fail.  See the
+ * examples above.
  */
 void visit_start_list(Visitor *v, const char *name, GenericList **list,
                       size_t size, Error **errp);
@@ -324,12 +327,14 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
 /*
  * Complete a list visit started earlier.
  *
+ * @list must match what was passed to the paired visit_start_list().
+ *
  * 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);
+void visit_end_list(Visitor *v, void **list);


 /*** Visiting alternates ***/
@@ -347,8 +352,9 @@ void visit_end_list(Visitor *v);
  *
  * If @promote_int, treat integers as QTYPE_FLOAT.
  *
- * If successful, this must be paired with visit_end_alternate() to
- * clean up, even if visiting the contents of the alternate fails.
+ * If successful, this must be paired with visit_end_alternate() with
+ * the same @obj 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,
@@ -357,15 +363,15 @@ void visit_start_alternate(Visitor *v, const char *name,
 /*
  * Finish visiting an alternate type.
  *
+ * @obj must match what was passed to the paired visit_start_alternate().
+ *
  * 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);
+void visit_end_alternate(Visitor *v, void **obj);


 /*** Other helpers ***/
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 145afd0..a495bf0 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -47,7 +47,7 @@ struct Visitor
     void (*check_struct)(Visitor *v, Error **errp);

     /* Must be set to visit structs */
-    void (*end_struct)(Visitor *v);
+    void (*end_struct)(Visitor *v, void **obj);

     /* Must be set; implementations may require @list to be non-null,
      * but must document it. */
@@ -58,7 +58,7 @@ struct Visitor
     GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);

     /* Must be set */
-    void (*end_list)(Visitor *v);
+    void (*end_list)(Visitor *v, void **list);

     /* Must be set by input and dealloc visitors to visit alternates;
      * optional for output visitors. */
@@ -67,7 +67,7 @@ struct Visitor
                             bool promote_int, Error **errp);

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

     /* Must be set */
     void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 8c6acb3..971dc4e 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -129,7 +129,7 @@ def gen_marshal(name, arg_type, ret_type):
     if (!err) {
         visit_check_struct(v, &err);
     }
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
     if (err) {
         goto out;
     }
@@ -160,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);
+    visit_end_struct(v, NULL);
     qapi_dealloc_visitor_cleanup(qdv);
 ''',
                      c_name=arg_type.c_name())
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 21fb167..084c40a 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -101,7 +101,7 @@ def gen_event_send(name, arg_type):
     if (!err) {
         visit_check_struct(v, &err);
     }
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
     if (err) {
         goto out;
     }
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 70ea8ca..7b85d2b 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -129,7 +129,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         }
     }

-    visit_end_list(v);
+    visit_end_list(v, (void **)obj);
     if (err && visit_is_input(v)) {
         qapi_free_%(c_name)s(*obj);
         *obj = NULL;
@@ -191,7 +191,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         if (!err) {
             visit_check_struct(v, &err);
         }
-        visit_end_struct(v);
+        visit_end_struct(v, NULL);
 ''',
                          c_type=var.type.c_name(),
                          c_name=c_name(var.name))
@@ -210,7 +210,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "%(name)s");
     }
-    visit_end_alternate(v);
+    visit_end_alternate(v, (void **)obj);
     if (err && visit_is_input(v)) {
         qapi_free_%(c_name)s(*obj);
         *obj = NULL;
@@ -244,7 +244,7 @@ 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);
+    visit_end_struct(v, (void **)obj);
     if (err && visit_is_input(v)) {
         qapi_free_%(c_name)s(*obj);
         *obj = NULL;
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index eada467..dba11c6 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -43,9 +43,9 @@ void visit_check_struct(Visitor *v, Error **errp)
     }
 }

-void visit_end_struct(Visitor *v)
+void visit_end_struct(Visitor *v, void **obj)
 {
-    v->end_struct(v);
+    v->end_struct(v, obj);
 }

 void visit_start_list(Visitor *v, const char *name, GenericList **list,
@@ -67,9 +67,9 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size)
     return v->next_list(v, tail, size);
 }

-void visit_end_list(Visitor *v)
+void visit_end_list(Visitor *v, void **obj)
 {
-    v->end_list(v);
+    v->end_list(v, obj);
 }

 void visit_start_alternate(Visitor *v, const char *name,
@@ -89,10 +89,10 @@ void visit_start_alternate(Visitor *v, const char *name,
     error_propagate(errp, err);
 }

-void visit_end_alternate(Visitor *v)
+void visit_end_alternate(Visitor *v, void **obj)
 {
     if (v->end_alternate) {
-        v->end_alternate(v);
+        v->end_alternate(v, obj);
     }
 }

diff --git a/block/crypto.c b/block/crypto.c
index 758e14e..5f0ab4d 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -222,7 +222,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
         visit_check_struct(opts_get_visitor(ov), &local_err);
     }

-    visit_end_struct(opts_get_visitor(ov));
+    visit_end_struct(opts_get_visitor(ov), NULL);

  out:
     if (local_err) {
@@ -269,7 +269,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
         visit_check_struct(opts_get_visitor(ov), &local_err);
     }

-    visit_end_struct(opts_get_visitor(ov));
+    visit_end_struct(opts_get_visitor(ov), NULL);

  out:
     if (local_err) {
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 94c875d..c1da698 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -298,7 +298,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
             /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
             g_assert(fdt_depth > 0);
             visit_check_struct(v, &err);
-            visit_end_struct(v);
+            visit_end_struct(v, NULL);
             if (err) {
                 error_propagate(errp, err);
                 return;
@@ -321,7 +321,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
                     return;
                 }
             }
-            visit_end_list(v);
+            visit_end_list(v, NULL);
             break;
         }
         default:
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 8c15e09..3c1d03d 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -143,13 +143,13 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
     }
     visit_check_struct(v, &err);
 out_nested:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);

     if (!err) {
         visit_check_struct(v, &err);
     }
 out_end:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 out:
     error_propagate(errp, err);
 }
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 4cf1cf8..dcfbf92 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -180,7 +180,7 @@ opts_check_struct(Visitor *v, Error **errp)


 static void
-opts_end_struct(Visitor *v)
+opts_end_struct(Visitor *v, void **obj)
 {
     OptsVisitor *ov = to_ov(v);

@@ -273,7 +273,7 @@ opts_next_list(Visitor *v, GenericList *tail, size_t size)


 static void
-opts_end_list(Visitor *v)
+opts_end_list(Visitor *v, void **obj)
 {
     OptsVisitor *ov = to_ov(v);

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index cd68b55..9391dea 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -19,53 +19,18 @@
 #include "qapi/qmp/types.h"
 #include "qapi/visitor-impl.h"

-typedef struct StackEntry
-{
-    void *value;
-    QTAILQ_ENTRY(StackEntry) node;
-} StackEntry;
-
 struct QapiDeallocVisitor
 {
     Visitor visitor;
-    QTAILQ_HEAD(, StackEntry) stack;
 };

-static QapiDeallocVisitor *to_qov(Visitor *v)
-{
-    return container_of(v, QapiDeallocVisitor, visitor);
-}
-
-static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value)
-{
-    StackEntry *e = g_malloc0(sizeof(*e));
-
-    e->value = value;
-
-    QTAILQ_INSERT_HEAD(&qov->stack, e, node);
-}
-
-static void *qapi_dealloc_pop(QapiDeallocVisitor *qov)
-{
-    StackEntry *e = QTAILQ_FIRST(&qov->stack);
-    QObject *value;
-    QTAILQ_REMOVE(&qov->stack, e, node);
-    value = e->value;
-    g_free(e);
-    return value;
-}
-
 static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj,
                                       size_t unused, Error **errp)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    qapi_dealloc_push(qov, obj);
 }

-static void qapi_dealloc_end_struct(Visitor *v)
+static void qapi_dealloc_end_struct(Visitor *v, void **obj)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    void **obj = qapi_dealloc_pop(qov);
     if (obj) {
         g_free(*obj);
     }
@@ -75,14 +40,10 @@ static void qapi_dealloc_start_alternate(Visitor *v, const char *name,
                                          GenericAlternate **obj, size_t size,
                                          bool promote_int, Error **errp)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    qapi_dealloc_push(qov, obj);
 }

-static void qapi_dealloc_end_alternate(Visitor *v)
+static void qapi_dealloc_end_alternate(Visitor *v, void **obj)
 {
-    QapiDeallocVisitor *qov = to_qov(v);
-    void **obj = qapi_dealloc_pop(qov);
     if (obj) {
         g_free(*obj);
     }
@@ -102,7 +63,7 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail,
     return next;
 }

-static void qapi_dealloc_end_list(Visitor *v)
+static void qapi_dealloc_end_list(Visitor *v, void **obj)
 {
 }

@@ -178,7 +139,5 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_null = qapi_dealloc_type_null;

-    QTAILQ_INIT(&v->stack);
-
     return v;
 }
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index aea90a1..84f32fc 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -145,7 +145,7 @@ static void qmp_input_check_struct(Visitor *v, Error **errp)
     }
 }

-static void qmp_input_pop(Visitor *v)
+static void qmp_input_pop(Visitor *v, void **obj)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 4d3cf78..8f44d23 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -107,7 +107,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)
+static void qmp_output_end_struct(Visitor *v, void **obj)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QObject *value = qmp_output_pop(qov);
@@ -131,7 +131,7 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail,
     return tail->next;
 }

-static void qmp_output_end_list(Visitor *v)
+static void qmp_output_end_list(Visitor *v, void **end)
 {
     QmpOutputVisitor *qov = to_qov(v);
     QObject *value = qmp_output_pop(qov);
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index b546e5f..3b0b788 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -172,7 +172,7 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
     return tail->next;
 }

-static void end_list(Visitor *v)
+static void end_list(Visitor *v, void **obj)
 {
 }

diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 5ea395a..cec3e76 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -291,7 +291,7 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
     return ret;
 }

-static void end_list(Visitor *v)
+static void end_list(Visitor *v, void **obj)
 {
     StringOutputVisitor *sov = to_sov(v);

diff --git a/qom/object.c b/qom/object.c
index 3bc8a00..1562f7e 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -2038,7 +2038,7 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name,
     }
     visit_check_struct(v, &err);
 out_end:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 out:
     error_propagate(errp, err);

diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 51e62e2..926ec68 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -71,7 +71,7 @@ Object *user_creatable_add(const QDict *qdict,
     obj = user_creatable_add_type(type, id, pdict, v, &local_err);

 out_visit:
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);

 out:
     QDECREF(pdict);
@@ -127,7 +127,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
     if (!local_err) {
         visit_check_struct(v, &local_err);
     }
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
     if (local_err) {
         goto out;
     }
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index d02306c..56ee943 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -305,7 +305,7 @@ static void test_visitor_in_null(TestInputVisitorData *data,
     visit_type_null(v, "b", &err);
     error_free_or_abort(&err);
     visit_check_struct(v, &error_abort);
-    visit_end_struct(v);
+    visit_end_struct(v, NULL);
 }

 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 9103fef..80d8c65 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -500,7 +500,7 @@ 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_check_struct(data->ov, &error_abort);
-    visit_end_struct(data->ov);
+    visit_end_struct(data->ov, NULL);
     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 d7d6987..79bfdf5 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -904,7 +904,7 @@ Example:
         }
         visit_check_struct(v, &err);
     out_obj:
-        visit_end_struct(v);
+        visit_end_struct(v, (void **)obj);
         if (err && visit_is_input(v)) {
             qapi_free_UserDefOne(*obj);
             *obj = NULL;
@@ -932,7 +932,7 @@ Example:
             }
         }

-        visit_end_list(v);
+        visit_end_list(v, (void **)obj);
         if (err && visit_is_input(v)) {
             qapi_free_UserDefOneList(*obj);
             *obj = NULL;
@@ -1022,7 +1022,7 @@ Example:
         if (!err) {
             visit_check_struct(v, &err);
         }
-        visit_end_struct(v);
+        visit_end_struct(v, NULL);
         if (err) {
             goto out;
         }
@@ -1041,7 +1041,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);
+        visit_end_struct(v, NULL);
         qapi_dealloc_visitor_cleanup(qdv);
     }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (3 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_* Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 16:03   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor " Eric Blake
                   ` (26 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Making each visitor provide its own (awkwardly-named) FOO_cleanup()
is unusual, when we can instead have a polymorphic visit_free()
interface.

The dealloc visitor is the first one converted to completely use
the new entry point, since only generated code and the testsuite
were using it.  Diffs to the generated code look like:

| void qapi_free_ACPIOSTInfo(ACPIOSTInfo *obj)
| {
|-    QapiDeallocVisitor *qdv;
|     Visitor *v;
|
|     if (!obj) {
|         return;
|     }
|
|-    qdv = qapi_dealloc_visitor_new();
|-    v = qapi_dealloc_get_visitor(qdv);
|+    v = qapi_dealloc_visitor_new();
|     visit_type_ACPIOSTInfo(v, NULL, &obj, NULL);
|-    qapi_dealloc_visitor_cleanup(qdv);
|+    visit_free(v);
|}

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

---
v4: new patch, inspired by review of v3 7/18
---
 include/qapi/visitor.h             | 37 ++++++++++++++++++++++++++++++++++---
 include/qapi/visitor-impl.h        |  3 +++
 scripts/qapi-commands.py           | 16 ++++++----------
 scripts/qapi-event.py              |  2 +-
 scripts/qapi-types.py              |  6 ++----
 include/qapi/dealloc-visitor.h     |  5 +----
 qapi/qapi-visit-core.c             |  7 +++++++
 qapi/opts-visitor.c                |  9 +++++++++
 qapi/qapi-dealloc-visitor.c        | 12 ++++--------
 qapi/qmp-input-visitor.c           |  7 +++++++
 qapi/qmp-output-visitor.c          |  7 +++++++
 qapi/string-input-visitor.c        |  7 +++++++
 qapi/string-output-visitor.c       |  7 +++++++
 tests/test-visitor-serialization.c |  6 +++---
 docs/qapi-code-gen.txt             | 28 ++++++++++------------------
 15 files changed, 108 insertions(+), 51 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 25d0cc2..2ded852 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -37,6 +37,24 @@
  * implemented by each visitor, and docs/qapi-code-gen.txt for more
  * about the QAPI code generator.
  *
+ * All of the visitors are created via:
+ *
+ * Type *subtype_visitor_new(parameters...);
+ *
+ * where Type is either directly 'Visitor *', or is a subtype that can
+ * be trivially upcast to Visitor * via another function:
+ *
+ * Visitor *subtype_get_visitor(SubtypeVisitor *);
+ *
+ * A visitor should be used for exactly one top-level visit_type_FOO()
+ * or virtual walk, then passed to visit_free() to clean up resources.
+ * It is okay to free the visitor without completing the visit, if
+ * some other error is detected in the meantime.  Output visitors
+ * provide an additional function, for collecting the final results of
+ * a successful visit: string_output_get_string() and
+ * qmp_output_get_qobject(); this collection function should not be
+ * called if any errors were reported during the visit.
+ *
  * All QAPI types have a corresponding function with a signature
  * roughly compatible with this:
  *
@@ -222,6 +240,19 @@ typedef struct GenericAlternate {
     char padding[];
 } GenericAlternate;

+/*** Visitor cleanup ***/
+
+/*
+ * Free @v and any resources it has tied up.
+ *
+ * May be called whether or not the visit has been successfully
+ * completed, but should not be called until a top-level
+ * visit_type_FOO() or visit_start_ITEM() has been performed on the
+ * visitor.  Safe if @v is NULL.
+ */
+void visit_free(Visitor *v);
+
+
 /*** Visiting structures ***/

 /*
@@ -272,7 +303,7 @@ void visit_check_struct(Visitor *v, Error **errp);
  * 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.
+ * with visit_free() behaves as if this was implicitly called.
  */
 void visit_end_struct(Visitor *v, void **obj);

@@ -332,7 +363,7 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
  * 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.
+ * with visit_free() behaves as if this was implicitly called.
  */
 void visit_end_list(Visitor *v, void **list);

@@ -368,7 +399,7 @@ void visit_start_alternate(Visitor *v, const char *name,
  * 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.
+ * with visit_free() behaves as if this was implicitly called.
  *
  */
 void visit_end_alternate(Visitor *v, void **obj);
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index a495bf0..525b068 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -104,6 +104,9 @@ struct Visitor

     /* Must be set */
     VisitorType type;
+
+    /* Must be set */
+    void (*free)(Visitor *v);
 };

 #endif
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 971dc4e..77ecd47 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -62,7 +62,6 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out,
 {
     Error *err = NULL;
     QmpOutputVisitor *qov = qmp_output_visitor_new();
-    QapiDeallocVisitor *qdv;
     Visitor *v;

     v = qmp_output_get_visitor(qov);
@@ -74,11 +73,10 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out,

 out:
     error_propagate(errp, err);
-    qmp_output_visitor_cleanup(qov);
-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
+    visit_free(v);
+    v = qapi_dealloc_visitor_new();
     visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_free(v);
 }
 ''',
                  c_type=ret_type.c_type(), c_name=ret_type.c_name())
@@ -116,7 +114,6 @@ def gen_marshal(name, arg_type, ret_type):
     if arg_type and arg_type.members:
         ret += mcgen('''
     QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
-    QapiDeallocVisitor *qdv;
     Visitor *v;
     %(c_name)s arg = {0};

@@ -155,13 +152,12 @@ out:
 ''')
     if arg_type and arg_type.members:
         ret += mcgen('''
-    qmp_input_visitor_cleanup(qiv);
-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
+    visit_free(v);
+    v = qapi_dealloc_visitor_new();
     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);
+    visit_free(v);
 ''',
                      c_name=arg_type.c_name())

diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 084c40a..909e8d6 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -119,7 +119,7 @@ def gen_event_send(name, arg_type):
     if arg_type and arg_type.members:
         ret += mcgen('''
 out:
-    qmp_output_visitor_cleanup(qov);
+    visit_free(v);
 ''')
     ret += mcgen('''
     error_propagate(errp, err);
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 437cf6c..5ace2cf 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -150,17 +150,15 @@ def gen_type_cleanup(name):

 void qapi_free_%(c_name)s(%(c_name)s *obj)
 {
-    QapiDeallocVisitor *qdv;
     Visitor *v;

     if (!obj) {
         return;
     }

-    qdv = qapi_dealloc_visitor_new();
-    v = qapi_dealloc_get_visitor(qdv);
+    v = qapi_dealloc_visitor_new();
     visit_type_%(c_name)s(v, NULL, &obj, NULL);
-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_free(v);
 }
 ''',
                 c_name=c_name(name))
diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h
index 45b06b2..b3e5c85 100644
--- a/include/qapi/dealloc-visitor.h
+++ b/include/qapi/dealloc-visitor.h
@@ -23,9 +23,6 @@ typedef struct QapiDeallocVisitor QapiDeallocVisitor;
  * 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);
-
-Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v);
+Visitor *qapi_dealloc_visitor_new(void);

 #endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index dba11c6..5f68c25 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -20,6 +20,13 @@
 #include "qapi/visitor.h"
 #include "qapi/visitor-impl.h"

+void visit_free(Visitor *v)
+{
+    if (v) {
+        v->free(v);
+    }
+}
+
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp)
 {
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index dcfbf92..28d2203 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -513,6 +513,14 @@ opts_optional(Visitor *v, const char *name, bool *present)
 }


+static void
+opts_free(Visitor *v)
+{
+    OptsVisitor *ov = to_ov(v);
+    opts_visitor_cleanup(ov);
+}
+
+
 OptsVisitor *
 opts_visitor_new(const QemuOpts *opts)
 {
@@ -540,6 +548,7 @@ opts_visitor_new(const QemuOpts *opts)
      * skip some mandatory methods... */

     ov->visitor.optional = &opts_optional;
+    ov->visitor.free = opts_free;

     ov->opts_root = opts;

diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 9391dea..235e8a1 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -107,17 +107,12 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
 {
 }

-Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
-{
-    return &v->visitor;
-}
-
-void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
+static void qapi_dealloc_free(Visitor *v)
 {
     g_free(v);
 }

-QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
+Visitor *qapi_dealloc_visitor_new(void)
 {
     QapiDeallocVisitor *v;

@@ -138,6 +133,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_number = qapi_dealloc_type_number;
     v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_null = qapi_dealloc_type_null;
+    v->visitor.free = qapi_dealloc_free;

-    return v;
+    return &v->visitor;
 }
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 84f32fc..3ca192e 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -375,6 +375,12 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
     return &v->visitor;
 }

+static void qmp_input_free(Visitor *v)
+{
+    QmpInputVisitor *qiv = to_qiv(v);
+    qmp_input_visitor_cleanup(qiv);
+}
+
 void qmp_input_visitor_cleanup(QmpInputVisitor *v)
 {
     qobject_decref(v->root);
@@ -403,6 +409,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->visitor.type_any = qmp_input_type_any;
     v->visitor.type_null = qmp_input_type_null;
     v->visitor.optional = qmp_input_optional;
+    v->visitor.free = qmp_input_free;
     v->strict = strict;

     v->root = obj;
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 8f44d23..7c6a601 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -209,6 +209,12 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
     return &v->visitor;
 }

+static void qmp_output_free(Visitor *v)
+{
+    QmpOutputVisitor *qov = to_qov(v);
+    qmp_output_visitor_cleanup(qov);
+}
+
 void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
 {
     QStackEntry *e, *tmp;
@@ -241,6 +247,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.type_number = qmp_output_type_number;
     v->visitor.type_any = qmp_output_type_any;
     v->visitor.type_null = qmp_output_type_null;
+    v->visitor.free = qmp_output_free;

     QTAILQ_INIT(&v->stack);

diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 3b0b788..dce5b42 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -330,6 +330,12 @@ Visitor *string_input_get_visitor(StringInputVisitor *v)
     return &v->visitor;
 }

+static void string_input_free(Visitor *v)
+{
+    StringInputVisitor *siv = to_siv(v);
+    string_input_visitor_cleanup(siv);
+}
+
 void string_input_visitor_cleanup(StringInputVisitor *v)
 {
     g_list_foreach(v->ranges, free_range, NULL);
@@ -354,6 +360,7 @@ StringInputVisitor *string_input_visitor_new(const char *str)
     v->visitor.next_list = next_list;
     v->visitor.end_list = end_list;
     v->visitor.optional = parse_optional;
+    v->visitor.free = string_input_free;

     v->string = str;
     return v;
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index cec3e76..8923898 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -319,6 +319,12 @@ static void free_range(void *range, void *dummy)
     g_free(range);
 }

+static void string_output_free(Visitor *v)
+{
+    StringOutputVisitor *sov = to_sov(v);
+    string_output_visitor_cleanup(sov);
+}
+
 void string_output_visitor_cleanup(StringOutputVisitor *sov)
 {
     if (sov->string) {
@@ -348,6 +354,7 @@ StringOutputVisitor *string_output_visitor_new(bool human)
     v->visitor.start_list = start_list;
     v->visitor.next_list = next_list;
     v->visitor.end_list = end_list;
+    v->visitor.free = string_output_free;

     return v;
 }
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index d102fa6..f83d019 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -90,11 +90,11 @@ typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp);

 static void dealloc_helper(void *native_in, VisitorFunc visit, Error **errp)
 {
-    QapiDeallocVisitor *qdv = qapi_dealloc_visitor_new();
+    Visitor *v = qapi_dealloc_visitor_new();

-    visit(qapi_dealloc_get_visitor(qdv), &native_in, errp);
+    visit(v, &native_in, errp);

-    qapi_dealloc_visitor_cleanup(qdv);
+    visit_free(v);
 }

 static void visit_primitive_type(Visitor *v, void **native, Error **errp)
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 79bfdf5..2830e02 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -802,32 +802,28 @@ Example:

     void qapi_free_UserDefOne(UserDefOne *obj)
     {
-        QapiDeallocVisitor *qdv;
         Visitor *v;

         if (!obj) {
             return;
         }

-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        v = qapi_dealloc_visitor_new();
         visit_type_UserDefOne(v, NULL, &obj, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_free(v);
     }

     void qapi_free_UserDefOneList(UserDefOneList *obj)
     {
-        QapiDeallocVisitor *qdv;
         Visitor *v;

         if (!obj) {
             return;
         }

-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        v = qapi_dealloc_visitor_new();
         visit_type_UserDefOneList(v, NULL, &obj, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_free(v);
     }

 === scripts/qapi-visit.py ===
@@ -985,7 +981,6 @@ Example:
     {
         Error *err = NULL;
         QmpOutputVisitor *qov = qmp_output_visitor_new();
-        QapiDeallocVisitor *qdv;
         Visitor *v;

         v = qmp_output_get_visitor(qov);
@@ -997,11 +992,10 @@ Example:

     out:
         error_propagate(errp, err);
-        qmp_output_visitor_cleanup(qov);
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        visit_free(v);
+        v = qapi_dealloc_visitor_new();
         visit_type_UserDefOne(v, "unused", &ret_in, NULL);
-        qapi_dealloc_visitor_cleanup(qdv);
+        visit_free(v);
     }

     static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp)
@@ -1009,7 +1003,6 @@ Example:
         Error *err = NULL;
         UserDefOne *retval;
         QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
-        QapiDeallocVisitor *qdv;
         Visitor *v;
         UserDefOneList *arg1 = NULL;

@@ -1036,13 +1029,12 @@ Example:

     out:
         error_propagate(errp, err);
-        qmp_input_visitor_cleanup(qiv);
-        qdv = qapi_dealloc_visitor_new();
-        v = qapi_dealloc_get_visitor(qdv);
+        visit_free(v);
+        v = qapi_dealloc_visitor_new();
         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);
+        visit_free(v);
     }

     static void qmp_init_marshal(void)
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor new visit_free() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (4 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 16:06   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 07/28] string-input-visitor: " Eric Blake
                   ` (25 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Luiz Capitulino,
	Michael S. Tsirkin, Igor Mammedov, Michael Roth, Jason Wang,
	Eduardo Habkost, Andreas Färber, open list:Block layer core

Now that we have a polymorphic visit_free(), we no longer need
opts_visitor_cleanup(); which in turn means we no longer need
to return a subtype from opts_visitor_new() nor a public upcast
function.

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

---
v4: new patch
---
 include/qapi/opts-visitor.h |  4 +---
 block/crypto.c              | 30 ++++++++++++++----------------
 hmp.c                       |  8 ++++----
 hw/acpi/core.c              |  8 ++++----
 net/net.c                   |  5 ++---
 numa.c                      |  6 +++---
 qapi/opts-visitor.c         | 26 ++++++--------------------
 qom/object_interfaces.c     |  8 ++++----
 tests/test-opts-visitor.c   |  9 ++++-----
 9 files changed, 42 insertions(+), 62 deletions(-)

diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h
index ae1bf7c..6462c96 100644
--- a/include/qapi/opts-visitor.h
+++ b/include/qapi/opts-visitor.h
@@ -35,8 +35,6 @@ typedef struct OptsVisitor OptsVisitor;
  * 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);
-Visitor *opts_get_visitor(OptsVisitor *nv);
+Visitor *opts_visitor_new(const QemuOpts *opts);

 #endif
diff --git a/block/crypto.c b/block/crypto.c
index 5f0ab4d..9bb55d3 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -193,17 +193,16 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
                             QemuOpts *opts,
                             Error **errp)
 {
-    OptsVisitor *ov;
+    Visitor *v;
     QCryptoBlockOpenOptions *ret = NULL;
     Error *local_err = NULL;

     ret = g_new0(QCryptoBlockOpenOptions, 1);
     ret->format = format;

-    ov = opts_visitor_new(opts);
+    v = opts_visitor_new(opts);

-    visit_start_struct(opts_get_visitor(ov),
-                       NULL, NULL, 0, &local_err);
+    visit_start_struct(v, NULL, NULL, 0, &local_err);
     if (local_err) {
         goto out;
     }
@@ -211,7 +210,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
     switch (format) {
     case Q_CRYPTO_BLOCK_FORMAT_LUKS:
         visit_type_QCryptoBlockOptionsLUKS_members(
-            opts_get_visitor(ov), &ret->u.luks, &local_err);
+            v, &ret->u.luks, &local_err);
         break;

     default:
@@ -219,10 +218,10 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
         break;
     }
     if (!local_err) {
-        visit_check_struct(opts_get_visitor(ov), &local_err);
+        visit_check_struct(v, &local_err);
     }

-    visit_end_struct(opts_get_visitor(ov), NULL);
+    visit_end_struct(v, NULL);

  out:
     if (local_err) {
@@ -230,7 +229,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format,
         qapi_free_QCryptoBlockOpenOptions(ret);
         ret = NULL;
     }
-    opts_visitor_cleanup(ov);
+    visit_free(v);
     return ret;
 }

@@ -240,17 +239,16 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
                               QemuOpts *opts,
                               Error **errp)
 {
-    OptsVisitor *ov;
+    Visitor *v;
     QCryptoBlockCreateOptions *ret = NULL;
     Error *local_err = NULL;

     ret = g_new0(QCryptoBlockCreateOptions, 1);
     ret->format = format;

-    ov = opts_visitor_new(opts);
+    v = opts_visitor_new(opts);

-    visit_start_struct(opts_get_visitor(ov),
-                       NULL, NULL, 0, &local_err);
+    visit_start_struct(v, NULL, NULL, 0, &local_err);
     if (local_err) {
         goto out;
     }
@@ -258,7 +256,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
     switch (format) {
     case Q_CRYPTO_BLOCK_FORMAT_LUKS:
         visit_type_QCryptoBlockCreateOptionsLUKS_members(
-            opts_get_visitor(ov), &ret->u.luks, &local_err);
+            v, &ret->u.luks, &local_err);
         break;

     default:
@@ -266,10 +264,10 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
         break;
     }
     if (!local_err) {
-        visit_check_struct(opts_get_visitor(ov), &local_err);
+        visit_check_struct(v, &local_err);
     }

-    visit_end_struct(opts_get_visitor(ov), NULL);
+    visit_end_struct(v, NULL);

  out:
     if (local_err) {
@@ -277,7 +275,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format,
         qapi_free_QCryptoBlockCreateOptions(ret);
         ret = NULL;
     }
-    opts_visitor_cleanup(ov);
+    visit_free(v);
     return ret;
 }

diff --git a/hmp.c b/hmp.c
index d510236..eef208c 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1675,7 +1675,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
     QemuOpts *opts;
-    OptsVisitor *ov;
+    Visitor *v;
     Object *obj = NULL;

     opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
@@ -1684,9 +1684,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict)
         return;
     }

-    ov = opts_visitor_new(opts);
-    obj = user_creatable_add(qdict, opts_get_visitor(ov), &err);
-    opts_visitor_cleanup(ov);
+    v = opts_visitor_new(opts);
+    obj = user_creatable_add(qdict, v, &err);
+    visit_free(v);
     qemu_opts_del(opts);

     if (err) {
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index 6a2f452..1303f51 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -239,11 +239,11 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
     char unsigned *blob = NULL;

     {
-        OptsVisitor *ov;
+        Visitor *v;

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

     if (err) {
diff --git a/net/net.c b/net/net.c
index 0ad6217..b411770 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1026,8 +1026,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
     void *object = NULL;
     Error *err = NULL;
     int ret = -1;
-    OptsVisitor *ov = opts_visitor_new(opts);
-    Visitor *v = opts_get_visitor(ov);
+    Visitor *v = opts_visitor_new(opts);

     {
         /* Parse convenience option format ip6-net=fec0::0[/64] */
@@ -1077,7 +1076,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
     }

     error_propagate(errp, err);
-    opts_visitor_cleanup(ov);
+    visit_free(v);
     return ret;
 }

diff --git a/numa.c b/numa.c
index 572712c..cbae430 100644
--- a/numa.c
+++ b/numa.c
@@ -217,9 +217,9 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
     Error *err = NULL;

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

     if (err) {
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 28d2203..6e65317 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -517,11 +517,15 @@ static void
 opts_free(Visitor *v)
 {
     OptsVisitor *ov = to_ov(v);
-    opts_visitor_cleanup(ov);
+    if (ov->unprocessed_opts != NULL) {
+        g_hash_table_destroy(ov->unprocessed_opts);
+    }
+    g_free(ov->fake_id_opt);
+    g_free(ov);
 }


-OptsVisitor *
+Visitor *
 opts_visitor_new(const QemuOpts *opts)
 {
     OptsVisitor *ov;
@@ -552,23 +556,5 @@ opts_visitor_new(const QemuOpts *opts)

     ov->opts_root = opts;

-    return ov;
-}
-
-
-void
-opts_visitor_cleanup(OptsVisitor *ov)
-{
-    if (ov->unprocessed_opts != NULL) {
-        g_hash_table_destroy(ov->unprocessed_opts);
-    }
-    g_free(ov->fake_id_opt);
-    g_free(ov);
-}
-
-
-Visitor *
-opts_get_visitor(OptsVisitor *ov)
-{
     return &ov->visitor;
 }
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 926ec68..bf59846 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -156,15 +156,15 @@ out:

 Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
 {
-    OptsVisitor *ov;
+    Visitor *v;
     QDict *pdict;
     Object *obj = NULL;

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

-    obj = user_creatable_add(pdict, opts_get_visitor(ov), errp);
-    opts_visitor_cleanup(ov);
+    obj = user_creatable_add(pdict, v, errp);
+    visit_free(v);
     QDECREF(pdict);
     return obj;
 }
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index 008e677..f46d902 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -38,16 +38,15 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data)
 {
     const char *opts_string = test_data;
     QemuOpts *opts;
-    OptsVisitor *ov;
+    Visitor *v;

     opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, false,
                            NULL);
     g_assert(opts != NULL);

-    ov = opts_visitor_new(opts);
-    visit_type_UserDefOptions(opts_get_visitor(ov), NULL, &f->userdef,
-                              &f->err);
-    opts_visitor_cleanup(ov);
+    v = opts_visitor_new(opts);
+    visit_type_UserDefOptions(v, NULL, &f->userdef, &f->err);
+    visit_free(v);
     qemu_opts_del(opts);
 }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 07/28] string-input-visitor: Favor new visit_free() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (5 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor " Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 16:13   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: " Eric Blake
                   ` (24 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth, Andreas Färber

Now that we have a polymorphic visit_free(), we no longer need
string_input_visitor_cleanup(); which in turn means we no longer
need to return a subtype from string_input_visitor_new() nor a
public upcast function.

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

---
v4: new patch
---
 include/qapi/string-input-visitor.h |  5 +----
 qapi/string-input-visitor.c         | 19 +++++--------------
 qom/object.c                        | 25 +++++++++++--------------
 tests/test-string-input-visitor.c   | 22 +++++++---------------
 tests/test-visitor-serialization.c  |  6 +++---
 5 files changed, 27 insertions(+), 50 deletions(-)

diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h
index 7b76c2b..3355134 100644
--- a/include/qapi/string-input-visitor.h
+++ b/include/qapi/string-input-visitor.h
@@ -22,9 +22,6 @@ typedef struct StringInputVisitor StringInputVisitor;
  * 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);
-
-Visitor *string_input_get_visitor(StringInputVisitor *v);
+Visitor *string_input_visitor_new(const char *str);

 #endif
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index dce5b42..a359e8e 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -325,25 +325,16 @@ static void parse_optional(Visitor *v, const char *name, bool *present)
     *present = true;
 }

-Visitor *string_input_get_visitor(StringInputVisitor *v)
-{
-    return &v->visitor;
-}
-
 static void string_input_free(Visitor *v)
 {
     StringInputVisitor *siv = to_siv(v);
-    string_input_visitor_cleanup(siv);
-}

-void string_input_visitor_cleanup(StringInputVisitor *v)
-{
-    g_list_foreach(v->ranges, free_range, NULL);
-    g_list_free(v->ranges);
-    g_free(v);
+    g_list_foreach(siv->ranges, free_range, NULL);
+    g_list_free(siv->ranges);
+    g_free(siv);
 }

-StringInputVisitor *string_input_visitor_new(const char *str)
+Visitor *string_input_visitor_new(const char *str)
 {
     StringInputVisitor *v;

@@ -363,5 +354,5 @@ StringInputVisitor *string_input_visitor_new(const char *str)
     v->visitor.free = string_input_free;

     v->string = str;
-    return v;
+    return &v->visitor;
 }
diff --git a/qom/object.c b/qom/object.c
index 1562f7e..00dd68c 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1216,7 +1216,7 @@ int object_property_get_enum(Object *obj, const char *name,
 {
     Error *err = NULL;
     StringOutputVisitor *sov;
-    StringInputVisitor *siv;
+    Visitor *v;
     char *str;
     int ret;
     ObjectProperty *prop = object_property_find(obj, name, errp);
@@ -1243,13 +1243,12 @@ int object_property_get_enum(Object *obj, const char *name,
         return 0;
     }
     str = string_output_get_string(sov);
-    siv = string_input_visitor_new(str);
     string_output_visitor_cleanup(sov);
-    visit_type_enum(string_input_get_visitor(siv), name, &ret,
-                    enumprop->strings, errp);
+    v = string_input_visitor_new(str);
+    visit_type_enum(v, name, &ret, enumprop->strings, errp);

     g_free(str);
-    string_input_visitor_cleanup(siv);
+    visit_free(v);

     return ret;
 }
@@ -1259,7 +1258,7 @@ void object_property_get_uint16List(Object *obj, const char *name,
 {
     Error *err = NULL;
     StringOutputVisitor *ov;
-    StringInputVisitor *iv;
+    Visitor *v;
     char *str;

     ov = string_output_visitor_new(false);
@@ -1270,11 +1269,11 @@ void object_property_get_uint16List(Object *obj, const char *name,
         goto out;
     }
     str = string_output_get_string(ov);
-    iv = string_input_visitor_new(str);
-    visit_type_uint16List(string_input_get_visitor(iv), NULL, list, errp);
+    v = string_input_visitor_new(str);
+    visit_type_uint16List(v, NULL, list, errp);

     g_free(str);
-    string_input_visitor_cleanup(iv);
+    visit_free(v);
 out:
     string_output_visitor_cleanup(ov);
 }
@@ -1282,11 +1281,9 @@ out:
 void object_property_parse(Object *obj, const char *string,
                            const char *name, Error **errp)
 {
-    StringInputVisitor *siv;
-    siv = string_input_visitor_new(string);
-    object_property_set(obj, string_input_get_visitor(siv), name, errp);
-
-    string_input_visitor_cleanup(siv);
+    Visitor *v = string_input_visitor_new(string);
+    object_property_set(obj, v, name, errp);
+    visit_free(v);
 }

 char *object_property_print(Object *obj, const char *name, bool human,
diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
index 5a56920..5af4898 100644
--- a/tests/test-string-input-visitor.c
+++ b/tests/test-string-input-visitor.c
@@ -21,15 +21,15 @@
 #include "qapi/qmp/types.h"

 typedef struct TestInputVisitorData {
-    StringInputVisitor *siv;
+    Visitor *v;
 } TestInputVisitorData;

 static void visitor_input_teardown(TestInputVisitorData *data,
                                    const void *unused)
 {
-    if (data->siv) {
-        string_input_visitor_cleanup(data->siv);
-        data->siv = NULL;
+    if (data->v) {
+        visit_free(data->v);
+        data->v = NULL;
     }
 }

@@ -40,15 +40,9 @@ static
 Visitor *visitor_input_test_init(TestInputVisitorData *data,
                                  const char *string)
 {
-    Visitor *v;
-
-    data->siv = string_input_visitor_new(string);
-    g_assert(data->siv != NULL);
-
-    v = string_input_get_visitor(data->siv);
-    g_assert(v != NULL);
-
-    return v;
+    data->v = string_input_visitor_new(string);
+    g_assert(data->v);
+    return data->v;
 }

 static void test_visitor_in_int(TestInputVisitorData *data,
@@ -200,8 +194,6 @@ static void test_visitor_in_enum(TestInputVisitorData *data,

         visitor_input_teardown(data, NULL);
     }
-
-    data->siv = NULL;
 }

 /* Try to crash the visitors */
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index f83d019..49107f7 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1057,7 +1057,7 @@ static void qmp_cleanup(void *datap)
 typedef struct StringSerializeData {
     char *string;
     StringOutputVisitor *sov;
-    StringInputVisitor *siv;
+    Visitor *siv;
 } StringSerializeData;

 static void string_serialize(void *native_in, void **datap,
@@ -1077,7 +1077,7 @@ static void string_deserialize(void **native_out, void *datap,

     d->string = string_output_get_string(d->sov);
     d->siv = string_input_visitor_new(d->string);
-    visit(string_input_get_visitor(d->siv), native_out, errp);
+    visit(d->siv, native_out, errp);
 }

 static void string_cleanup(void *datap)
@@ -1085,7 +1085,7 @@ static void string_cleanup(void *datap)
     StringSerializeData *d = datap;

     string_output_visitor_cleanup(d->sov);
-    string_input_visitor_cleanup(d->siv);
+    visit_free(d->siv);
     g_free(d->string);
     g_free(d);
 }
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: Favor new visit_free() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (6 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 07/28] string-input-visitor: " Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 16:19   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 09/28] string-output-visitor: " Eric Blake
                   ` (23 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, Andreas Färber, Daniel P. Berrange,
	Gerd Hoffmann, Paolo Bonzini

Now that we have a polymorphic visit_free(), we no longer need
qmp_input_visitor_cleanup(); which in turn means we no longer
need to return a subtype from qmp_input_visitor_new() nor a
public upcast function.

Generated code changes to qmp-marshal look like:

|@@ -52,11 +52,10 @@ void qmp_marshal_add_fd(QDict *args, QOb
| {
|     Error *err = NULL;
|     AddfdInfo *retval;
|-    QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
|     Visitor *v;
|     q_obj_add_fd_arg arg = {0};
|
|-    v = qmp_input_get_visitor(qiv);
|+    v = qmp_input_visitor_new(QOBJECT(args), true);
|     visit_start_struct(v, NULL, NULL, 0, &err);
|     if (err) {
|         goto out;

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

---
v4: new patch
---
 scripts/qapi-commands.py           |  3 +--
 include/qapi/qmp-input-visitor.h   |  6 +-----
 qapi/qmp-input-visitor.c           | 17 ++++-------------
 qmp.c                              |  9 ++++-----
 qom/qom-qobject.c                  |  9 ++++-----
 replay/replay-input.c              |  6 ++----
 tests/check-qnull.c                |  6 +++---
 tests/test-qmp-commands.c          |  8 ++++----
 tests/test-qmp-input-strict.c      | 12 +++---------
 tests/test-qmp-input-visitor.c     | 12 +++---------
 tests/test-visitor-serialization.c |  6 +++---
 util/qemu-sockets.c                |  6 ++----
 docs/qapi-code-gen.txt             |  3 +--
 13 files changed, 35 insertions(+), 68 deletions(-)

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 77ecd47..49d4ce2 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -113,11 +113,10 @@ def gen_marshal(name, arg_type, ret_type):

     if arg_type and arg_type.members:
         ret += mcgen('''
-    QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
     Visitor *v;
     %(c_name)s arg = {0};

-    v = qmp_input_get_visitor(qiv);
+    v = qmp_input_visitor_new(QOBJECT(args), true);
     visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h
index b0624d8..f3ff5f3 100644
--- a/include/qapi/qmp-input-visitor.h
+++ b/include/qapi/qmp-input-visitor.h
@@ -25,10 +25,6 @@ typedef struct QmpInputVisitor QmpInputVisitor;
  * 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);
-
-Visitor *qmp_input_get_visitor(QmpInputVisitor *v);
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict);

 #endif
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 3ca192e..cb128e2 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -370,24 +370,15 @@ static void qmp_input_optional(Visitor *v, const char *name, bool *present)
     *present = true;
 }

-Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
-{
-    return &v->visitor;
-}
-
 static void qmp_input_free(Visitor *v)
 {
     QmpInputVisitor *qiv = to_qiv(v);
-    qmp_input_visitor_cleanup(qiv);
-}

-void qmp_input_visitor_cleanup(QmpInputVisitor *v)
-{
-    qobject_decref(v->root);
-    g_free(v);
+    qobject_decref(qiv->root);
+    g_free(qiv);
 }

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

@@ -415,5 +406,5 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->root = obj;
     qobject_incref(obj);

-    return v;
+    return &v->visitor;
 }
diff --git a/qmp.c b/qmp.c
index e784a67..8c7a051 100644
--- a/qmp.c
+++ b/qmp.c
@@ -652,7 +652,7 @@ void qmp_object_add(const char *type, const char *id,
                     bool has_props, QObject *props, Error **errp)
 {
     const QDict *pdict = NULL;
-    QmpInputVisitor *qiv;
+    Visitor *v;
     Object *obj;

     if (props) {
@@ -663,10 +663,9 @@ void qmp_object_add(const char *type, const char *id,
         }
     }

-    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);
+    v = qmp_input_visitor_new(props, true);
+    obj = user_creatable_add_type(type, id, pdict, v, errp);
+    visit_free(v);
     if (obj) {
         object_unref(obj);
     }
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index b66088d..c3c9188 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -21,12 +21,11 @@
 void object_property_set_qobject(Object *obj, QObject *value,
                                  const char *name, Error **errp)
 {
-    QmpInputVisitor *qiv;
+    Visitor *v;
     /* 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);
-
-    qmp_input_visitor_cleanup(qiv);
+    v = qmp_input_visitor_new(value, false);
+    object_property_set(obj, v, name, errp);
+    visit_free(v);
 }

 QObject *object_property_get_qobject(Object *obj, const char *name,
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 03e99d5..7b6fa93 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -23,7 +23,6 @@
 static InputEvent *qapi_clone_InputEvent(InputEvent *src)
 {
     QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
     Visitor *ov, *iv;
     QObject *obj;
     InputEvent *dst = NULL;
@@ -37,10 +36,9 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)
         return NULL;
     }

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

     return dst;
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index fd9c68f..c8021dc 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -39,7 +39,7 @@ static void qnull_visit_test(void)
 {
     QObject *obj;
     QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
+    Visitor *qiv;

     /*
      * Most tests of interactions between QObject and visitors are in
@@ -51,8 +51,8 @@ static void qnull_visit_test(void)
     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);
+    visit_type_null(qiv, NULL, &error_abort);
+    visit_free(qiv);

     qov = qmp_output_visitor_new();
     visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 5c3edd7..87fc759 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -217,14 +217,14 @@ static void test_dealloc_partial(void)
     /* create partial object */
     {
         QDict *ud2_dict;
-        QmpInputVisitor *qiv;
+        Visitor *v;

         ud2_dict = qdict_new();
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));

-        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);
+        v = qmp_input_visitor_new(QOBJECT(ud2_dict), true);
+        visit_type_UserDefTwo(v, NULL, &ud2, &err);
+        visit_free(v);
         QDECREF(ud2_dict);
     }

diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index edb4b31..89947f5 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -27,7 +27,7 @@

 typedef struct TestInputVisitorData {
     QObject *obj;
-    QmpInputVisitor *qiv;
+    Visitor *qiv;
 } TestInputVisitorData;

 static void validate_teardown(TestInputVisitorData *data,
@@ -37,7 +37,7 @@ static void validate_teardown(TestInputVisitorData *data,
     data->obj = NULL;

     if (data->qiv) {
-        qmp_input_visitor_cleanup(data->qiv);
+        visit_free(data->qiv);
         data->qiv = NULL;
     }
 }
@@ -49,8 +49,6 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data,
                                             const char *json_string,
                                             va_list *ap)
 {
-    Visitor *v;
-
     validate_teardown(data, NULL);

     data->obj = qobject_from_jsonv(json_string, ap);
@@ -58,11 +56,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data,

     data->qiv = qmp_input_visitor_new(data->obj, true);
     g_assert(data->qiv);
-
-    v = qmp_input_get_visitor(data->qiv);
-    g_assert(v);
-
-    return v;
+    return data->qiv;
 }

 static GCC_FMT_ATTR(2, 3)
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 56ee943..9137e10 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -23,7 +23,7 @@

 typedef struct TestInputVisitorData {
     QObject *obj;
-    QmpInputVisitor *qiv;
+    Visitor *qiv;
 } TestInputVisitorData;

 static void visitor_input_teardown(TestInputVisitorData *data,
@@ -33,7 +33,7 @@ static void visitor_input_teardown(TestInputVisitorData *data,
     data->obj = NULL;

     if (data->qiv) {
-        qmp_input_visitor_cleanup(data->qiv);
+        visit_free(data->qiv);
         data->qiv = NULL;
     }
 }
@@ -45,8 +45,6 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
                                                  const char *json_string,
                                                  va_list *ap)
 {
-    Visitor *v;
-
     visitor_input_teardown(data, NULL);

     data->obj = qobject_from_jsonv(json_string, ap);
@@ -54,11 +52,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,

     data->qiv = qmp_input_visitor_new(data->obj, false);
     g_assert(data->qiv);
-
-    v = qmp_input_get_visitor(data->qiv);
-    g_assert(v);
-
-    return v;
+    return data->qiv;
 }

 static GCC_FMT_ATTR(2, 3)
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 49107f7..bc1afd3 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1014,7 +1014,7 @@ static PrimitiveType pt_values[] = {

 typedef struct QmpSerializeData {
     QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
+    Visitor *qiv;
 } QmpSerializeData;

 static void qmp_serialize(void *native_in, void **datap,
@@ -1042,14 +1042,14 @@ static void qmp_deserialize(void **native_out, void *datap,
     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);
+    visit(d->qiv, native_out, errp);
 }

 static void qmp_cleanup(void *datap)
 {
     QmpSerializeData *d = datap;
     qmp_output_visitor_cleanup(d->qov);
-    qmp_input_visitor_cleanup(d->qiv);
+    visit_free(d->qiv);

     g_free(d);
 }
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 2a2c524..5b55eb2 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1130,7 +1130,6 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,
                              SocketAddress *src)
 {
     QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
     Visitor *ov, *iv;
     QObject *obj;

@@ -1145,9 +1144,8 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,
         return;
     }

-    qiv = qmp_input_visitor_new(obj, true);
-    iv = qmp_input_get_visitor(qiv);
+    iv = qmp_input_visitor_new(obj, true);
     visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
-    qmp_input_visitor_cleanup(qiv);
+    visit_free(iv);
     qobject_decref(obj);
 }
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 2830e02..aefc29e 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1002,11 +1002,10 @@ Example:
     {
         Error *err = NULL;
         UserDefOne *retval;
-        QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
         Visitor *v;
         UserDefOneList *arg1 = NULL;

-        v = qmp_input_get_visitor(qiv);
+        v = qmp_input_visitor_new(QOBJECT(args), true);
         visit_start_struct(v, NULL, NULL, 0, &err);
         if (err) {
             goto out;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 09/28] string-output-visitor: Favor new visit_free() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (7 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: " Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 10/28] qmp-output-visitor: " Eric Blake
                   ` (22 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Luiz Capitulino, Michael Roth, Jason Wang, Andreas Färber

Now that we have a polymorphic visit_free(), we no longer need
string_output_visitor_cleanup(); however, we still need to
expose the subtype for string_output_get_string().

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

---
v4: new patch
---
 include/qapi/string-output-visitor.h |  1 -
 hmp.c                                |  2 +-
 net/net.c                            |  2 +-
 qapi/string-output-visitor.c         |  4 ----
 qom/object.c                         | 11 ++++++-----
 tests/test-string-output-visitor.c   |  2 +-
 tests/test-visitor-serialization.c   |  4 ++--
 7 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
index e10522a..03e377e 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -23,7 +23,6 @@ typedef struct StringOutputVisitor StringOutputVisitor;
  * requires a non-null list argument to visit_start_list().
  */
 StringOutputVisitor *string_output_visitor_new(bool human);
-void string_output_visitor_cleanup(StringOutputVisitor *v);

 char *string_output_get_string(StringOutputVisitor *v);
 Visitor *string_output_get_visitor(StringOutputVisitor *v);
diff --git a/hmp.c b/hmp.c
index eef208c..4a0a502 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1954,7 +1954,7 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
         monitor_printf(mon, "  host nodes: %s\n", str);

         g_free(str);
-        string_output_visitor_cleanup(ov);
+        visit_free(string_output_get_visitor(ov));
         m = m->next;
         i++;
     }
diff --git a/net/net.c b/net/net.c
index b411770..557800d 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1212,7 +1212,7 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
         object_property_get(OBJECT(nf), string_output_get_visitor(ov),
                             prop->name, NULL);
         str = string_output_get_string(ov);
-        string_output_visitor_cleanup(ov);
+        visit_free(string_output_get_visitor(ov));
         monitor_printf(mon, ",%s=%s", prop->name, str);
         g_free(str);
     }
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 8923898..91ee694 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -322,11 +322,7 @@ static void free_range(void *range, void *dummy)
 static void string_output_free(Visitor *v)
 {
     StringOutputVisitor *sov = to_sov(v);
-    string_output_visitor_cleanup(sov);
-}

-void string_output_visitor_cleanup(StringOutputVisitor *sov)
-{
     if (sov->string) {
         g_string_free(sov->string, true);
     }
diff --git a/qom/object.c b/qom/object.c
index 00dd68c..5c834eb 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1236,14 +1236,15 @@ int object_property_get_enum(Object *obj, const char *name,
     enumprop = prop->opaque;

     sov = string_output_visitor_new(false);
-    object_property_get(obj, string_output_get_visitor(sov), name, &err);
+    v = string_output_get_visitor(sov);
+    object_property_get(obj, v, name, &err);
     if (err) {
         error_propagate(errp, err);
-        string_output_visitor_cleanup(sov);
+        visit_free(v);
         return 0;
     }
     str = string_output_get_string(sov);
-    string_output_visitor_cleanup(sov);
+    visit_free(v);
     v = string_input_visitor_new(str);
     visit_type_enum(v, name, &ret, enumprop->strings, errp);

@@ -1275,7 +1276,7 @@ void object_property_get_uint16List(Object *obj, const char *name,
     g_free(str);
     visit_free(v);
 out:
-    string_output_visitor_cleanup(ov);
+    visit_free(string_output_get_visitor(ov));
 }

 void object_property_parse(Object *obj, const char *string,
@@ -1303,7 +1304,7 @@ char *object_property_print(Object *obj, const char *name, bool human,
     string = string_output_get_string(sov);

 out:
-    string_output_visitor_cleanup(sov);
+    visit_free(string_output_get_visitor(sov));
     return string;
 }

diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index 1ecd75b..2a62a2f 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -51,7 +51,7 @@ static void visitor_output_setup_human(TestOutputVisitorData *data,
 static void visitor_output_teardown(TestOutputVisitorData *data,
                                     const void *unused)
 {
-    string_output_visitor_cleanup(data->sov);
+    visit_free(data->ov);
     data->sov = NULL;
     data->ov = NULL;
 }
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index bc1afd3..63027e1 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1048,7 +1048,7 @@ static void qmp_deserialize(void **native_out, void *datap,
 static void qmp_cleanup(void *datap)
 {
     QmpSerializeData *d = datap;
-    qmp_output_visitor_cleanup(d->qov);
+    visit_free(qmp_output_get_visitor(d->qov));
     visit_free(d->qiv);

     g_free(d);
@@ -1084,7 +1084,7 @@ static void string_cleanup(void *datap)
 {
     StringSerializeData *d = datap;

-    string_output_visitor_cleanup(d->sov);
+    visit_free(string_output_get_visitor(d->sov));
     visit_free(d->siv);
     g_free(d->string);
     g_free(d);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 10/28] qmp-output-visitor: Favor new visit_free() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (8 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 09/28] string-output-visitor: " Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests Eric Blake
                   ` (21 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Michael Roth, Andreas Färber,
	Daniel P. Berrange, Gerd Hoffmann, Paolo Bonzini,
	open list:Block layer core

Now that we have a polymorphic visit_free(), we no longer need
qmp_output_visitor_cleanup(); however, we still need to
expose the subtype for qmp_output_get_qobject().

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

---
v4: new patch
---
 include/qapi/qmp-output-visitor.h |  1 -
 block/qapi.c                      |  2 +-
 blockdev.c                        |  2 +-
 qapi/qmp-output-visitor.c         | 13 ++++---------
 qemu-img.c                        |  6 +++---
 qom/qom-qobject.c                 |  2 +-
 replay/replay-input.c             |  2 +-
 tests/check-qnull.c               |  2 +-
 tests/test-qmp-output-visitor.c   |  2 +-
 util/qemu-sockets.c               |  2 +-
 10 files changed, 14 insertions(+), 20 deletions(-)

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

 QmpOutputVisitor *qmp_output_visitor_new(void);
-void qmp_output_visitor_cleanup(QmpOutputVisitor *v);

 QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
 Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
diff --git a/block/qapi.c b/block/qapi.c
index c5f6ba6..f46d879 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -699,7 +699,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
     assert(qobject_type(obj) == QTYPE_QDICT);
     data = qdict_get(qobject_to_qdict(obj), "data");
     dump_qobject(func_fprintf, f, 1, data);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(qmp_output_get_visitor(ov));
 }

 void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
diff --git a/blockdev.c b/blockdev.c
index 1892b8e..783e796 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -4009,7 +4009,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
     }

 fail:
-    qmp_output_visitor_cleanup(ov);
+    visit_free(qmp_output_get_visitor(ov));
 }

 void qmp_x_blockdev_del(bool has_id, const char *id,
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 7c6a601..80bc682 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -212,20 +212,15 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
 static void qmp_output_free(Visitor *v)
 {
     QmpOutputVisitor *qov = to_qov(v);
-    qmp_output_visitor_cleanup(qov);
-}
-
-void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
-{
     QStackEntry *e, *tmp;

-    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
-        QTAILQ_REMOVE(&v->stack, e, node);
+    QTAILQ_FOREACH_SAFE(e, &qov->stack, node, tmp) {
+        QTAILQ_REMOVE(&qov->stack, e, node);
         g_free(e);
     }

-    qobject_decref(v->root);
-    g_free(v);
+    qobject_decref(qov->root);
+    g_free(qov);
 }

 QmpOutputVisitor *qmp_output_visitor_new(void)
diff --git a/qemu-img.c b/qemu-img.c
index 3aa7fb3..58c1949 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -489,7 +489,7 @@ static void dump_json_image_check(ImageCheck *check, bool quiet)
     assert(str != NULL);
     qprintf(quiet, "%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(qmp_output_get_visitor(ov));
     QDECREF(str);
 }

@@ -2179,7 +2179,7 @@ static void dump_json_image_info_list(ImageInfoList *list)
     assert(str != NULL);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(qmp_output_get_visitor(ov));
     QDECREF(str);
 }

@@ -2195,7 +2195,7 @@ static void dump_json_image_info(ImageInfo *info)
     assert(str != NULL);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(ov);
+    visit_free(qmp_output_get_visitor(ov));
     QDECREF(str);
 }

diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index c3c9188..6714565 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -41,6 +41,6 @@ QObject *object_property_get_qobject(Object *obj, const char *name,
         ret = qmp_output_get_qobject(qov);
     }
     error_propagate(errp, local_err);
-    qmp_output_visitor_cleanup(qov);
+    visit_free(qmp_output_get_visitor(qov));
     return ret;
 }
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 7b6fa93..9cbb4c1 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -31,7 +31,7 @@ static InputEvent *qapi_clone_InputEvent(InputEvent *src)
     ov = qmp_output_get_visitor(qov);
     visit_type_InputEvent(ov, NULL, &src, &error_abort);
     obj = qmp_output_get_qobject(qov);
-    qmp_output_visitor_cleanup(qov);
+    visit_free(ov);
     if (!obj) {
         return NULL;
     }
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index c8021dc..fff8518 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -59,7 +59,7 @@ static void qnull_visit_test(void)
     obj = qmp_output_get_qobject(qov);
     g_assert(obj == &qnull_);
     qobject_decref(obj);
-    qmp_output_visitor_cleanup(qov);
+    visit_free(qmp_output_get_visitor(qov));

     g_assert(qnull_.refcnt == 1);
 }
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 80d8c65..d638ea6 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -39,7 +39,7 @@ static void visitor_output_setup(TestOutputVisitorData *data,
 static void visitor_output_teardown(TestOutputVisitorData *data,
                                     const void *unused)
 {
-    qmp_output_visitor_cleanup(data->qov);
+    visit_free(data->ov);
     data->qov = NULL;
     data->ov = NULL;
 }
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 5b55eb2..a34cb33 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1139,7 +1139,7 @@ void qapi_copy_SocketAddress(SocketAddress **p_dest,
     ov = qmp_output_get_visitor(qov);
     visit_type_SocketAddress(ov, NULL, &src, &error_abort);
     obj = qmp_output_get_qobject(qov);
-    qmp_output_visitor_cleanup(qov);
+    visit_free(ov);
     if (!obj) {
         return;
     }
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (9 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 10/28] qmp-output-visitor: " Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 16:33   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function Eric Blake
                   ` (20 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Create a new visitor_get() function to capture common
actions taken in collecting output from an output visitor,
to make it easier to refactor the output visitors in a
later patch. While making test improvements, also use
&error_abort and error_free_or_abort() in more places, use
the generated qapi_free_intList() instead of open-coding it,
reduce the scope of some variables, and copy the visitor_reset()
concept from the qmp-output test to the string-output test.

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

---
v4: new patch
---
 tests/test-qmp-output-visitor.c    | 78 +++++++++++-------------------
 tests/test-string-output-visitor.c | 97 ++++++++++++++++++--------------------
 2 files changed, 72 insertions(+), 103 deletions(-)

diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index d638ea6..0362192 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -24,6 +24,7 @@
 typedef struct TestOutputVisitorData {
     QmpOutputVisitor *qov;
     Visitor *ov;
+    QObject *obj;
 } TestOutputVisitorData;

 static void visitor_output_setup(TestOutputVisitorData *data,
@@ -42,6 +43,15 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
     visit_free(data->ov);
     data->qov = NULL;
     data->ov = NULL;
+    qobject_decref(data->obj);
+    data->obj = NULL;
+}
+
+static QObject *visitor_get(TestOutputVisitorData *data)
+{
+    data->obj = qmp_output_get_qobject(data->qov);
+    g_assert(data->obj);
+    return data->obj;
 }

 static void visitor_reset(TestOutputVisitorData *data)
@@ -58,12 +68,9 @@ static void test_visitor_out_int(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QINT);
     g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value);
-
-    qobject_decref(obj);
 }

 static void test_visitor_out_bool(TestOutputVisitorData *data,
@@ -74,12 +81,9 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QBOOL);
     g_assert(qbool_get_bool(qobject_to_qbool(obj)) == value);
-
-    qobject_decref(obj);
 }

 static void test_visitor_out_number(TestOutputVisitorData *data,
@@ -90,12 +94,9 @@ static void test_visitor_out_number(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QFLOAT);
     g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
-
-    qobject_decref(obj);
 }

 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -106,12 +107,9 @@ static void test_visitor_out_string(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
-
-    qobject_decref(obj);
 }

 static void test_visitor_out_no_string(TestOutputVisitorData *data,
@@ -123,12 +121,9 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
     /* A null string should return "" */
     visit_type_str(data->ov, NULL, &string, &error_abort);

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, "");
-
-    qobject_decref(obj);
 }

 static void test_visitor_out_enum(TestOutputVisitorData *data,
@@ -140,12 +135,10 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
     for (i = 0; i < ENUM_ONE__MAX; i++) {
         visit_type_EnumOne(data->ov, "unused", &i, &error_abort);

-        obj = qmp_output_get_qobject(data->qov);
-        g_assert(obj != NULL);
+        obj = visitor_get(data);
         g_assert(qobject_type(obj) == QTYPE_QSTRING);
         g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
                         EnumOne_lookup[i]);
-        qobject_decref(obj);
         visitor_reset(data);
     }
 }
@@ -178,8 +171,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QDICT);

     qdict = qobject_to_qdict(obj);
@@ -187,8 +179,6 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false);
     g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
-
-    QDECREF(qdict);
 }

 static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
@@ -223,8 +213,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QDICT);

     qdict = qobject_to_qdict(obj);
@@ -251,7 +240,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
     g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);

-    QDECREF(qdict);
     qapi_free_UserDefTwo(ud2);
 }

@@ -303,8 +291,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QLIST);

     qlist = qobject_to_qlist(obj);
@@ -325,7 +312,6 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
     }
     g_assert_cmpint(i, ==, max_items);

-    QDECREF(qlist);
     qapi_free_TestStructList(head);
 }

@@ -369,11 +355,9 @@ static void test_visitor_out_any(TestOutputVisitorData *data,

     qobj = QOBJECT(qint_from_int(-42));
     visit_type_any(data->ov, NULL, &qobj, &error_abort);
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
+    obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QINT);
     g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42);
-    qobject_decref(obj);
     qobject_decref(qobj);

     visitor_reset(data);
@@ -383,9 +367,8 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qdict_put(qdict, "string", qstring_from_str("foo"));
     qobj = QOBJECT(qdict);
     visit_type_any(data->ov, NULL, &qobj, &error_abort);
+    obj = visitor_get(data);
     qobject_decref(qobj);
-    obj = qmp_output_get_qobject(data->qov);
-    g_assert(obj != NULL);
     qdict = qobject_to_qdict(obj);
     g_assert(qdict);
     qobj = qdict_get(qdict, "integer");
@@ -403,7 +386,6 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
     qstring = qobject_to_qstring(qobj);
     g_assert(qstring);
     g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
-    qobject_decref(obj);
 }

 static void test_visitor_out_union_flat(TestOutputVisitorData *data,
@@ -419,7 +401,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     tmp->u.value1.boolean = true;

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

     g_assert(qobject_type(arg) == QTYPE_QDICT);
     qdict = qobject_to_qdict(arg);
@@ -430,7 +412,6 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);

     qapi_free_UserDefFlatUnion(tmp);
-    QDECREF(qdict);
 }

 static void test_visitor_out_alternate(TestOutputVisitorData *data,
@@ -445,13 +426,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->u.i = 42;

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

     g_assert(qobject_type(arg) == QTYPE_QINT);
     g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42);

     qapi_free_UserDefAlternate(tmp);
-    qobject_decref(arg);

     visitor_reset(data);
     tmp = g_new0(UserDefAlternate, 1);
@@ -459,13 +439,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->u.s = g_strdup("hello");

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

     g_assert(qobject_type(arg) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(arg)), ==, "hello");

     qapi_free_UserDefAlternate(tmp);
-    qobject_decref(arg);

     visitor_reset(data);
     tmp = g_new0(UserDefAlternate, 1);
@@ -476,7 +455,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     tmp->u.udfu.u.value1.boolean = true;

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

     g_assert_cmpint(qobject_type(arg), ==, QTYPE_QDICT);
     qdict = qobject_to_qdict(arg);
@@ -487,7 +466,6 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);

     qapi_free_UserDefAlternate(tmp);
-    qobject_decref(arg);
 }

 static void test_visitor_out_null(TestOutputVisitorData *data,
@@ -501,14 +479,13 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
     visit_type_null(data->ov, "a", &error_abort);
     visit_check_struct(data->ov, &error_abort);
     visit_end_struct(data->ov, NULL);
-    arg = qmp_output_get_qobject(data->qov);
+    arg = visitor_get(data);
     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);
 }

 static void init_native_list(UserDefNativeListUnion *cvalue)
@@ -739,10 +716,9 @@ static void test_native_list(TestOutputVisitorData *data,

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

-    obj = qmp_output_get_qobject(data->qov);
+    obj = visitor_get(data);
     check_native_list(obj, cvalue->type);
     qapi_free_UserDefNativeListUnion(cvalue);
-    qobject_decref(obj);
 }

 static void test_visitor_out_native_list_int(TestOutputVisitorData *data,
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index 2a62a2f..d5dd8c8 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -23,29 +23,30 @@
 typedef struct TestOutputVisitorData {
     StringOutputVisitor *sov;
     Visitor *ov;
+    char *str;
     bool human;
 } TestOutputVisitorData;

+static void visitor_output_setup_internal(TestOutputVisitorData *data,
+                                          bool human)
+{
+    data->human = human;
+    data->sov = string_output_visitor_new(human);
+    g_assert(data->sov);
+    data->ov = string_output_get_visitor(data->sov);
+    g_assert(data->ov);
+}
+
 static void visitor_output_setup(TestOutputVisitorData *data,
                                  const void *unused)
 {
-    data->human = false;
-    data->sov = string_output_visitor_new(data->human);
-    g_assert(data->sov != NULL);
-
-    data->ov = string_output_get_visitor(data->sov);
-    g_assert(data->ov != NULL);
+    return visitor_output_setup_internal(data, false);
 }

 static void visitor_output_setup_human(TestOutputVisitorData *data,
                                        const void *unused)
 {
-    data->human = true;
-    data->sov = string_output_visitor_new(data->human);
-    g_assert(data->sov != NULL);
-
-    data->ov = string_output_get_visitor(data->sov);
-    g_assert(data->ov != NULL);
+    return visitor_output_setup_internal(data, true);
 }

 static void visitor_output_teardown(TestOutputVisitorData *data,
@@ -54,6 +55,23 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
     visit_free(data->ov);
     data->sov = NULL;
     data->ov = NULL;
+    g_free(data->str);
+    data->str = NULL;
+}
+
+static char *visitor_get(TestOutputVisitorData *data)
+{
+    data->str = string_output_get_string(data->sov);
+    g_assert(data->str);
+    return data->str;
+}
+
+static void visitor_reset(TestOutputVisitorData *data)
+{
+    bool human = data->human;
+
+    visitor_output_teardown(data, NULL);
+    visitor_output_setup_internal(data, human);
 }

 static void test_visitor_out_int(TestOutputVisitorData *data,
@@ -66,14 +84,12 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
     visit_type_int(data->ov, NULL, &value, &err);
     g_assert(!err);

-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==, "42 (0x2a)");
     } else {
         g_assert_cmpstr(str, ==, "42");
     }
-    g_free(str);
 }

 static void test_visitor_out_intList(TestOutputVisitorData *data,
@@ -95,8 +111,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
     visit_type_intList(data->ov, NULL, &list, &err);
     g_assert(err == NULL);

-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==,
             "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 "
@@ -106,13 +121,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
         g_assert_cmpstr(str, ==,
             "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807");
     }
-    g_free(str);
-    while (list) {
-        intList *tmp2;
-        tmp2 = list->next;
-        g_free(list);
-        list = tmp2;
-    }
+    qapi_free_intList(list);
 }

 static void test_visitor_out_bool(TestOutputVisitorData *data,
@@ -125,10 +134,8 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
     visit_type_bool(data->ov, NULL, &value, &err);
     g_assert(!err);

-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     g_assert_cmpstr(str, ==, "true");
-    g_free(str);
 }

 static void test_visitor_out_number(TestOutputVisitorData *data,
@@ -141,10 +148,8 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
     visit_type_number(data->ov, NULL, &value, &err);
     g_assert(!err);

-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     g_assert_cmpstr(str, ==, "3.140000");
-    g_free(str);
 }

 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -158,61 +163,50 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
     visit_type_str(data->ov, NULL, &string, &err);
     g_assert(!err);

-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==, string_human);
     } else {
         g_assert_cmpstr(str, ==, string);
     }
-    g_free(str);
 }

 static void test_visitor_out_no_string(TestOutputVisitorData *data,
                                        const void *unused)
 {
     char *string = NULL;
-    Error *err = NULL;
     char *str;

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

-    str = string_output_get_string(data->sov);
-    g_assert(str != NULL);
+    str = visitor_get(data);
     if (data->human) {
         g_assert_cmpstr(str, ==, "<null>");
     } else {
         g_assert_cmpstr(str, ==, "");
     }
-    g_free(str);
 }

 static void test_visitor_out_enum(TestOutputVisitorData *data,
                                   const void *unused)
 {
-    Error *err = NULL;
     char *str;
     EnumOne i;

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

-        visit_type_EnumOne(data->ov, "unused", &i, &err);
-        g_assert(!err);
-
-        str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
-
-        str = string_output_get_string(data->sov);
-        g_assert(str != NULL);
+        str = visitor_get(data);
         if (data->human) {
+            char *str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
+
             g_assert_cmpstr(str, ==, str_human);
+            g_free(str_human);
         } else {
             g_assert_cmpstr(str, ==, EnumOne_lookup[i]);
         }
-        g_free(str_human);
-	g_free(str);
+        visitor_reset(data);
     }
 }

@@ -225,8 +219,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
     for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
         err = NULL;
         visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
-        g_assert(err);
-        error_free(err);
+        error_free_or_abort(&err);
     }
 }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (10 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-01 17:02   ` Markus Armbruster
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor Eric Blake
                   ` (19 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Luiz Capitulino, Michael Roth,
	Jason Wang, Andreas Färber, Daniel P. Berrange,
	Gerd Hoffmann, Paolo Bonzini, open list:Block layer core

Making each output visitor provide its own output collection
function was the only remaining reason for exposing visitor
sub-types to the rest of the code base.  Add a polymorphic
visit_complete() function which is a no-op for input visitors,
and which populates an opaque pointer for output visitors.  For
maximum type-safety, also add a parameter to the output visitor
constructors with a type-correct version of the output pointer,
and assert that the two uses match.

This approach was considered superior to either passing the
output parameter only during construction (action at a distance
during visit_free() feels awkward) or only during visit_complete()
(defeating type safety makes it easier to use incorrectly).

Most callers were function-local, and therefore a mechanical
conversion; the testsuite was a bit trickier, but the previous
cleanup patch minimized the churn here.

Generated code is simplified as follows for events:

|@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
|     QDict *qmp;
|     Error *err = NULL;
|     QMPEventFuncEmit emit;
|-    QmpOutputVisitor *qov;
|+    QObject *obj;
|     Visitor *v;
|     q_obj_ACPI_DEVICE_OST_arg param = {
|         info
|@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
|
|     qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
|
|-    qov = qmp_output_visitor_new();
|-    v = qmp_output_get_visitor(qov);
|+    v = qmp_output_visitor_new(&obj);
|
|     visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
|     if (err) {
|@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
|         goto out;
|     }
|
|-    qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
|+    visit_complete(v, &obj);
|+    qdict_put_obj(qmp, "data", obj);
|     emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);

and for commands:

| {
|     Error *err = NULL;
|-    QmpOutputVisitor *qov = qmp_output_visitor_new();
|     Visitor *v;
|
|-    v = qmp_output_get_visitor(qov);
|+    v = qmp_output_visitor_new(ret_out);
|     visit_type_AddfdInfo(v, "unused", &ret_in, &err);
|-    if (err) {
|-        goto out;
|+    if (!err) {
|+        visit_complete(v, ret_out);
|     }
|-    *ret_out = qmp_output_get_qobject(qov);
|-
|-out:
|     error_propagate(errp, err);

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

---
v4: new patch, inspired by review of v3 7/18
---
 include/qapi/visitor.h               | 52 +++++++++++++++++++++---------------
 include/qapi/visitor-impl.h          |  3 +++
 scripts/qapi-commands.py             | 10 +++----
 scripts/qapi-event.py                |  8 +++---
 include/qapi/qmp-output-visitor.h    | 11 +++++---
 include/qapi/string-output-visitor.h | 10 ++++---
 qapi/qapi-visit-core.c               | 10 +++++++
 block/qapi.c                         |  9 +++----
 blockdev.c                           |  9 +++----
 hmp.c                                | 11 ++++----
 net/net.c                            | 11 ++++----
 qapi/qmp-output-visitor.c            | 21 ++++++++-------
 qapi/string-output-visitor.c         | 22 ++++++++-------
 qemu-img.c                           | 30 ++++++++++-----------
 qom/object.c                         | 28 +++++++++----------
 qom/qom-qobject.c                    | 10 +++----
 replay/replay-input.c                |  6 ++---
 tests/check-qnull.c                  | 17 ++++++------
 tests/test-qmp-output-visitor.c      | 11 +++-----
 tests/test-string-output-visitor.c   |  8 ++----
 tests/test-visitor-serialization.c   | 22 ++++++++-------
 util/qemu-sockets.c                  |  6 ++---
 22 files changed, 167 insertions(+), 158 deletions(-)

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 2ded852..b3bd97c 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -39,21 +39,15 @@
  *
  * All of the visitors are created via:
  *
- * Type *subtype_visitor_new(parameters...);
- *
- * where Type is either directly 'Visitor *', or is a subtype that can
- * be trivially upcast to Visitor * via another function:
- *
- * Visitor *subtype_get_visitor(SubtypeVisitor *);
+ * Visitor *subtype_visitor_new(parameters...);
  *
  * A visitor should be used for exactly one top-level visit_type_FOO()
- * or virtual walk, then passed to visit_free() to clean up resources.
- * It is okay to free the visitor without completing the visit, if
- * some other error is detected in the meantime.  Output visitors
- * provide an additional function, for collecting the final results of
- * a successful visit: string_output_get_string() and
- * qmp_output_get_qobject(); this collection function should not be
- * called if any errors were reported during the visit.
+ * or virtual walk; if that is successful, the caller can optionally
+ * call visit_complete() (most useful for output visits).  Then,
+ * regardless of success or failure, the user should call visit_free()
+ * to clean up resources.  It is okay to free the visitor without
+ * completing the visit, if some other error is detected in the
+ * meantime.
  *
  * All QAPI types have a corresponding function with a signature
  * roughly compatible with this:
@@ -123,14 +117,14 @@
  *  Error *err = NULL;
  *  Visitor *v;
  *
- *  v = ...obtain input visitor...
+ *  v = FOO_visitor_new(...);
  *  visit_type_Foo(v, NULL, &f, &err);
  *  if (err) {
  *      ...handle error...
  *  } else {
  *      ...use f...
  *  }
- *  ...clean up v...
+ *  visit_free(v);
  *  qapi_free_Foo(f);
  * </example>
  *
@@ -140,7 +134,7 @@
  *  Error *err = NULL;
  *  Visitor *v;
  *
- *  v = ...obtain input visitor...
+ *  v = FOO_visitor_new(...);
  *  visit_type_FooList(v, NULL, &l, &err);
  *  if (err) {
  *      ...handle error...
@@ -149,7 +143,7 @@
  *          ...use l->value...
  *      }
  *  }
- *  ...clean up v...
+ *  visit_free(v);
  *  qapi_free_FooList(l);
  * </example>
  *
@@ -159,13 +153,17 @@
  *  Foo *f = ...obtain populated object...
  *  Error *err = NULL;
  *  Visitor *v;
+ *  Type *result;
  *
- *  v = ...obtain output visitor...
+ *  v = FOO_visitor_new(..., &result);
  *  visit_type_Foo(v, NULL, &f, &err);
  *  if (err) {
  *      ...handle error...
+ *  } else {
+ *      visit_complete(v, &result);
+ *      ...use result...
  *  }
- *  ...clean up v...
+ *  visit_free(v);
  * </example>
  *
  * When visiting a real QAPI struct, this file provides several
@@ -191,7 +189,7 @@
  *  Error *err = NULL;
  *  int value;
  *
- *  v = ...obtain visitor...
+ *  v = FOO_visitor_new(...);
  *  visit_start_struct(v, NULL, NULL, 0, &err);
  *  if (err) {
  *      goto out;
@@ -219,7 +217,7 @@
  *  visit_end_struct(v, NULL);
  * out:
  *  error_propagate(errp, err);
- *  ...clean up v...
+ *  visit_free(v);
  * </example>
  */

@@ -243,6 +241,18 @@ typedef struct GenericAlternate {
 /*** Visitor cleanup ***/

 /*
+ * Complete the visit, collecting any output.
+ *
+ * May only be called after a successful top-level visit_type_FOO() or
+ * visit_end_ITEM(), and marks the end of the visit.  The @opaque
+ * pointer should match the output parameter passed to the
+ * subtype_visitor_new() used to create an output visitor, or NULL for
+ * any other visitor.  Needed for output visitors, but may also be
+ * called with other visitors.
+ */
+void visit_complete(Visitor *v, void *opaque);
+
+/*
  * Free @v and any resources it has tied up.
  *
  * May be called whether or not the visit has been successfully
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 525b068..16e0b86 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -105,6 +105,9 @@ struct Visitor
     /* Must be set */
     VisitorType type;

+    /* Must be set for output visitors, optional otherwise. */
+    void (*complete)(Visitor *v, void *opaque);
+
     /* Must be set */
     void (*free)(Visitor *v);
 };
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 49d4ce2..34b6a3a 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -61,17 +61,13 @@ def gen_marshal_output(ret_type):
 static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
 {
     Error *err = NULL;
-    QmpOutputVisitor *qov = qmp_output_visitor_new();
     Visitor *v;

-    v = qmp_output_get_visitor(qov);
+    v = qmp_output_visitor_new(ret_out);
     visit_type_%(c_name)s(v, "unused", &ret_in, &err);
-    if (err) {
-        goto out;
+    if (!err) {
+        visit_complete(v, ret_out);
     }
-    *ret_out = qmp_output_get_qobject(qov);
-
-out:
     error_propagate(errp, err);
     visit_free(v);
     v = qapi_dealloc_visitor_new();
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 909e8d6..9c88627 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -71,7 +71,7 @@ def gen_event_send(name, arg_type):

     if arg_type and arg_type.members:
         ret += mcgen('''
-    QmpOutputVisitor *qov;
+    QObject *obj;
     Visitor *v;
 ''')
         ret += gen_param_var(arg_type)
@@ -90,8 +90,7 @@ def gen_event_send(name, arg_type):

     if arg_type and arg_type.members:
         ret += mcgen('''
-    qov = qmp_output_visitor_new();
-    v = qmp_output_get_visitor(qov);
+    v = qmp_output_visitor_new(&obj);

     visit_start_struct(v, "%(name)s", NULL, 0, &err);
     if (err) {
@@ -106,7 +105,8 @@ def gen_event_send(name, arg_type):
         goto out;
     }

-    qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
+    visit_complete(v, &obj);
+    qdict_put_obj(qmp, "data", obj);
 ''',
                      name=name, c_name=arg_type.c_name())

diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
index 29c9a2e..040fdda 100644
--- a/include/qapi/qmp-output-visitor.h
+++ b/include/qapi/qmp-output-visitor.h
@@ -19,9 +19,12 @@

 typedef struct QmpOutputVisitor QmpOutputVisitor;

-QmpOutputVisitor *qmp_output_visitor_new(void);
-
-QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
-Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
+/*
+ * Create a new QMP output visitor.
+ *
+ * If everything else succeeds, pass @result to visit_complete() to
+ * collect the result of the visit.
+ */
+Visitor *qmp_output_visitor_new(QObject **result);

 #endif
diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
index 03e377e..a31054e 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -18,13 +18,15 @@
 typedef struct StringOutputVisitor StringOutputVisitor;

 /*
+ * Create a new string output visitor.
+ *
+ * If everything else succeeds, pass @result to visit_complete() to
+ * collect the result of the visit.
+ *
  * The string output visitor does not implement support for visiting
  * 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);
-
-char *string_output_get_string(StringOutputVisitor *v);
-Visitor *string_output_get_visitor(StringOutputVisitor *v);
+Visitor *string_output_visitor_new(bool human, char **result);

 #endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 5f68c25..279ea8e 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -20,6 +20,16 @@
 #include "qapi/visitor.h"
 #include "qapi/visitor-impl.h"

+void visit_complete(Visitor *v, void *opaque)
+{
+    if (v->type == VISITOR_OUTPUT) {
+        assert(v->complete);
+    }
+    if (v->complete) {
+        v->complete(v, opaque);
+    }
+}
+
 void visit_free(Visitor *v)
 {
     if (v) {
diff --git a/block/qapi.c b/block/qapi.c
index f46d879..871e11b 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -690,16 +690,15 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
 void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
                                    ImageInfoSpecific *info_spec)
 {
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj, *data;
+    Visitor *v = qmp_output_visitor_new(&obj);

-    visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec,
-                                 &error_abort);
-    obj = qmp_output_get_qobject(ov);
+    visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
+    visit_complete(v, &obj);
     assert(qobject_type(obj) == QTYPE_QDICT);
     data = qdict_get(qobject_to_qdict(obj), "data");
     dump_qobject(func_fprintf, f, 1, data);
-    visit_free(qmp_output_get_visitor(ov));
+    visit_free(v);
 }

 void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
diff --git a/blockdev.c b/blockdev.c
index 783e796..adc5058 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3939,10 +3939,10 @@ out:

 void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
 {
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     BlockDriverState *bs;
     BlockBackend *blk = NULL;
     QObject *obj;
+    Visitor *v = qmp_output_visitor_new(&obj);
     QDict *qdict;
     Error *local_err = NULL;

@@ -3961,14 +3961,13 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
         }
     }

-    visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options,
-                               &local_err);
+    visit_type_BlockdevOptions(v, NULL, &options, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto fail;
     }

-    obj = qmp_output_get_qobject(ov);
+    visit_complete(v, &obj);
     qdict = qobject_to_qdict(obj);

     qdict_flatten(qdict);
@@ -4009,7 +4008,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
     }

 fail:
-    visit_free(qmp_output_get_visitor(ov));
+    visit_free(v);
 }

 void qmp_x_blockdev_del(bool has_id, const char *id,
diff --git a/hmp.c b/hmp.c
index 4a0a502..9836227 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1931,15 +1931,14 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
     Error *err = NULL;
     MemdevList *memdev_list = qmp_query_memdev(&err);
     MemdevList *m = memdev_list;
-    StringOutputVisitor *ov;
+    Visitor *v;
     char *str;
     int i = 0;


     while (m) {
-        ov = string_output_visitor_new(false);
-        visit_type_uint16List(string_output_get_visitor(ov), NULL,
-                              &m->value->host_nodes, NULL);
+        v = string_output_visitor_new(false, &str);
+        visit_type_uint16List(v, NULL, &m->value->host_nodes, NULL);
         monitor_printf(mon, "memory backend: %d\n", i);
         monitor_printf(mon, "  size:  %" PRId64 "\n", m->value->size);
         monitor_printf(mon, "  merge: %s\n",
@@ -1950,11 +1949,11 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
                        m->value->prealloc ? "true" : "false");
         monitor_printf(mon, "  policy: %s\n",
                        HostMemPolicy_lookup[m->value->policy]);
-        str = string_output_get_string(ov);
+        visit_complete(v, &str);
         monitor_printf(mon, "  host nodes: %s\n", str);

         g_free(str);
-        visit_free(string_output_get_visitor(ov));
+        visit_free(v);
         m = m->next;
         i++;
     }
diff --git a/net/net.c b/net/net.c
index 557800d..7a91e8f 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1200,7 +1200,7 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
     char *str;
     ObjectProperty *prop;
     ObjectPropertyIterator iter;
-    StringOutputVisitor *ov;
+    Visitor *v;

     /* generate info str */
     object_property_iter_init(&iter, OBJECT(nf));
@@ -1208,11 +1208,10 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
         if (!strcmp(prop->name, "type")) {
             continue;
         }
-        ov = string_output_visitor_new(false);
-        object_property_get(OBJECT(nf), string_output_get_visitor(ov),
-                            prop->name, NULL);
-        str = string_output_get_string(ov);
-        visit_free(string_output_get_visitor(ov));
+        v = string_output_visitor_new(false, &str);
+        object_property_get(OBJECT(nf), v, prop->name, NULL);
+        visit_complete(v, &str);
+        visit_free(v);
         monitor_printf(mon, ",%s=%s", prop->name, str);
         g_free(str);
     }
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 80bc682..2dac302 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -32,6 +32,7 @@ struct QmpOutputVisitor
     Visitor visitor;
     QStack stack; /* Stack of containers that haven't yet been finished */
     QObject *root; /* Root of the output visit */
+    QObject **result; /* User's storage location for result */
 };

 #define qmp_output_add(qov, name, value) \
@@ -195,18 +196,17 @@ static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
 /* 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)
+static void qmp_output_complete(Visitor *v, void *opaque)
 {
+    QmpOutputVisitor *qov = to_qov(v);
+
     /* A visit must have occurred, with each start paired with end.  */
     assert(qov->root && QTAILQ_EMPTY(&qov->stack));
+    assert(opaque == qov->result);

     qobject_incref(qov->root);
-    return qov->root;
-}
-
-Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
-{
-    return &v->visitor;
+    *qov->result = qov->root;
+    qov->result = NULL;
 }

 static void qmp_output_free(Visitor *v)
@@ -223,7 +223,7 @@ static void qmp_output_free(Visitor *v)
     g_free(qov);
 }

-QmpOutputVisitor *qmp_output_visitor_new(void)
+Visitor *qmp_output_visitor_new(QObject **result)
 {
     QmpOutputVisitor *v;

@@ -242,9 +242,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.type_number = qmp_output_type_number;
     v->visitor.type_any = qmp_output_type_any;
     v->visitor.type_null = qmp_output_type_null;
+    v->visitor.complete = qmp_output_complete;
     v->visitor.free = qmp_output_free;

     QTAILQ_INIT(&v->stack);
+    *result = NULL;
+    v->result = result;

-    return v;
+    return &v->visitor;
 }
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index 91ee694..c1c5942 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -58,6 +58,7 @@ struct StringOutputVisitor
     Visitor visitor;
     bool human;
     GString *string;
+    char **result;
     ListMode list_mode;
     union {
         int64_t s;
@@ -302,16 +303,13 @@ static void end_list(Visitor *v, void **obj)
     sov->list_mode = LM_NONE;
 }

-char *string_output_get_string(StringOutputVisitor *sov)
+static void string_output_complete(Visitor *v, void *opaque)
 {
-    char *string = g_string_free(sov->string, false);
+    StringOutputVisitor *sov = to_sov(v);
+
+    assert(opaque == sov->result);
+    *sov->result = g_string_free(sov->string, false);
     sov->string = NULL;
-    return string;
-}
-
-Visitor *string_output_get_visitor(StringOutputVisitor *sov)
-{
-    return &sov->visitor;
 }

 static void free_range(void *range, void *dummy)
@@ -332,7 +330,7 @@ static void string_output_free(Visitor *v)
     g_free(sov);
 }

-StringOutputVisitor *string_output_visitor_new(bool human)
+Visitor *string_output_visitor_new(bool human, char **result)
 {
     StringOutputVisitor *v;

@@ -340,6 +338,9 @@ StringOutputVisitor *string_output_visitor_new(bool human)

     v->string = g_string_new(NULL);
     v->human = human;
+    v->result = result;
+    *result = NULL;
+
     v->visitor.type = VISITOR_OUTPUT;
     v->visitor.type_int64 = print_type_int64;
     v->visitor.type_uint64 = print_type_uint64;
@@ -350,7 +351,8 @@ StringOutputVisitor *string_output_visitor_new(bool human)
     v->visitor.start_list = start_list;
     v->visitor.next_list = next_list;
     v->visitor.end_list = end_list;
+    v->visitor.complete = string_output_complete;
     v->visitor.free = string_output_free;

-    return v;
+    return &v->visitor;
 }
diff --git a/qemu-img.c b/qemu-img.c
index 58c1949..39bff77 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -480,16 +480,16 @@ fail:
 static void dump_json_image_check(ImageCheck *check, bool quiet)
 {
     QString *str;
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check,
-                          &error_abort);
-    obj = qmp_output_get_qobject(ov);
+    Visitor *v = qmp_output_visitor_new(&obj);
+
+    visit_type_ImageCheck(v, NULL, &check, &error_abort);
+    visit_complete(v, &obj);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
     qprintf(quiet, "%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    visit_free(qmp_output_get_visitor(ov));
+    visit_free(v);
     QDECREF(str);
 }

@@ -2170,32 +2170,32 @@ static void dump_snapshots(BlockDriverState *bs)
 static void dump_json_image_info_list(ImageInfoList *list)
 {
     QString *str;
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list,
-                             &error_abort);
-    obj = qmp_output_get_qobject(ov);
+    Visitor *v = qmp_output_visitor_new(&obj);
+
+    visit_type_ImageInfoList(v, NULL, &list, &error_abort);
+    visit_complete(v, &obj);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    visit_free(qmp_output_get_visitor(ov));
+    visit_free(v);
     QDECREF(str);
 }

 static void dump_json_image_info(ImageInfo *info)
 {
     QString *str;
-    QmpOutputVisitor *ov = qmp_output_visitor_new();
     QObject *obj;
-    visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info,
-                         &error_abort);
-    obj = qmp_output_get_qobject(ov);
+    Visitor *v = qmp_output_visitor_new(&obj);
+
+    visit_type_ImageInfo(v, NULL, &info, &error_abort);
+    visit_complete(v, &obj);
     str = qobject_to_json_pretty(obj);
     assert(str != NULL);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
-    visit_free(qmp_output_get_visitor(ov));
+    visit_free(v);
     QDECREF(str);
 }

diff --git a/qom/object.c b/qom/object.c
index 5c834eb..26e7d58 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1215,7 +1215,6 @@ int object_property_get_enum(Object *obj, const char *name,
                              const char *typename, Error **errp)
 {
     Error *err = NULL;
-    StringOutputVisitor *sov;
     Visitor *v;
     char *str;
     int ret;
@@ -1235,15 +1234,14 @@ int object_property_get_enum(Object *obj, const char *name,

     enumprop = prop->opaque;

-    sov = string_output_visitor_new(false);
-    v = string_output_get_visitor(sov);
+    v = string_output_visitor_new(false, &str);
     object_property_get(obj, v, name, &err);
     if (err) {
         error_propagate(errp, err);
         visit_free(v);
         return 0;
     }
-    str = string_output_get_string(sov);
+    visit_complete(v, &str);
     visit_free(v);
     v = string_input_visitor_new(str);
     visit_type_enum(v, name, &ret, enumprop->strings, errp);
@@ -1258,25 +1256,23 @@ void object_property_get_uint16List(Object *obj, const char *name,
                                     uint16List **list, Error **errp)
 {
     Error *err = NULL;
-    StringOutputVisitor *ov;
     Visitor *v;
     char *str;

-    ov = string_output_visitor_new(false);
-    object_property_get(obj, string_output_get_visitor(ov),
-                        name, &err);
+    v = string_output_visitor_new(false, &str);
+    object_property_get(obj, v, name, &err);
     if (err) {
         error_propagate(errp, err);
         goto out;
     }
-    str = string_output_get_string(ov);
+    visit_complete(v, &str);
+    visit_free(v);
     v = string_input_visitor_new(str);
     visit_type_uint16List(v, NULL, list, errp);

     g_free(str);
-    visit_free(v);
 out:
-    visit_free(string_output_get_visitor(ov));
+    visit_free(v);
 }

 void object_property_parse(Object *obj, const char *string,
@@ -1290,21 +1286,21 @@ void object_property_parse(Object *obj, const char *string,
 char *object_property_print(Object *obj, const char *name, bool human,
                             Error **errp)
 {
-    StringOutputVisitor *sov;
+    Visitor *v;
     char *string = NULL;
     Error *local_err = NULL;

-    sov = string_output_visitor_new(human);
-    object_property_get(obj, string_output_get_visitor(sov), name, &local_err);
+    v = string_output_visitor_new(human, &string);
+    object_property_get(obj, v, name, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         goto out;
     }

-    string = string_output_get_string(sov);
+    visit_complete(v, &string);

 out:
-    visit_free(string_output_get_visitor(sov));
+    visit_free(v);
     return string;
 }

diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index 6714565..c225abc 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -33,14 +33,14 @@ QObject *object_property_get_qobject(Object *obj, const char *name,
 {
     QObject *ret = NULL;
     Error *local_err = NULL;
-    QmpOutputVisitor *qov;
+    Visitor *v;

-    qov = qmp_output_visitor_new();
-    object_property_get(obj, qmp_output_get_visitor(qov), name, &local_err);
+    v = qmp_output_visitor_new(&ret);
+    object_property_get(obj, v, name, &local_err);
     if (!local_err) {
-        ret = qmp_output_get_qobject(qov);
+        visit_complete(v, &ret);
     }
     error_propagate(errp, local_err);
-    visit_free(qmp_output_get_visitor(qov));
+    visit_free(v);
     return ret;
 }
diff --git a/replay/replay-input.c b/replay/replay-input.c
index 9cbb4c1..296399c 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -22,15 +22,13 @@

 static InputEvent *qapi_clone_InputEvent(InputEvent *src)
 {
-    QmpOutputVisitor *qov;
     Visitor *ov, *iv;
     QObject *obj;
     InputEvent *dst = NULL;

-    qov = qmp_output_visitor_new();
-    ov = qmp_output_get_visitor(qov);
+    ov = qmp_output_visitor_new(&obj);
     visit_type_InputEvent(ov, NULL, &src, &error_abort);
-    obj = qmp_output_get_qobject(qov);
+    visit_complete(ov, &obj);
     visit_free(ov);
     if (!obj) {
         return NULL;
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index fff8518..0e59749 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -38,8 +38,7 @@ static void qnull_ref_test(void)
 static void qnull_visit_test(void)
 {
     QObject *obj;
-    QmpOutputVisitor *qov;
-    Visitor *qiv;
+    Visitor *v;

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

     g_assert(qnull_.refcnt == 1);
     obj = qnull();
-    qiv = qmp_input_visitor_new(obj, true);
+    v = qmp_input_visitor_new(obj, true);
     qobject_decref(obj);
-    visit_type_null(qiv, NULL, &error_abort);
-    visit_free(qiv);
+    visit_type_null(v, NULL, &error_abort);
+    visit_free(v);

-    qov = qmp_output_visitor_new();
-    visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort);
-    obj = qmp_output_get_qobject(qov);
+    v = qmp_output_visitor_new(&obj);
+    visit_type_null(v, NULL, &error_abort);
+    visit_complete(v, &obj);
     g_assert(obj == &qnull_);
     qobject_decref(obj);
-    visit_free(qmp_output_get_visitor(qov));
+    visit_free(v);

     g_assert(qnull_.refcnt == 1);
 }
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 0362192..df8a495 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -22,7 +22,6 @@
 #include "qapi/qmp/qobject-json.h"

 typedef struct TestOutputVisitorData {
-    QmpOutputVisitor *qov;
     Visitor *ov;
     QObject *obj;
 } TestOutputVisitorData;
@@ -30,18 +29,14 @@ typedef struct TestOutputVisitorData {
 static void visitor_output_setup(TestOutputVisitorData *data,
                                  const void *unused)
 {
-    data->qov = qmp_output_visitor_new();
-    g_assert(data->qov != NULL);
-
-    data->ov = qmp_output_get_visitor(data->qov);
-    g_assert(data->ov != NULL);
+    data->ov = qmp_output_visitor_new(&data->obj);
+    g_assert(data->ov);
 }

 static void visitor_output_teardown(TestOutputVisitorData *data,
                                     const void *unused)
 {
     visit_free(data->ov);
-    data->qov = NULL;
     data->ov = NULL;
     qobject_decref(data->obj);
     data->obj = NULL;
@@ -49,7 +44,7 @@ static void visitor_output_teardown(TestOutputVisitorData *data,

 static QObject *visitor_get(TestOutputVisitorData *data)
 {
-    data->obj = qmp_output_get_qobject(data->qov);
+    visit_complete(data->ov, &data->obj);
     g_assert(data->obj);
     return data->obj;
 }
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index d5dd8c8..7fe4351 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -21,7 +21,6 @@
 #include "qapi/qmp/types.h"

 typedef struct TestOutputVisitorData {
-    StringOutputVisitor *sov;
     Visitor *ov;
     char *str;
     bool human;
@@ -31,9 +30,7 @@ static void visitor_output_setup_internal(TestOutputVisitorData *data,
                                           bool human)
 {
     data->human = human;
-    data->sov = string_output_visitor_new(human);
-    g_assert(data->sov);
-    data->ov = string_output_get_visitor(data->sov);
+    data->ov = string_output_visitor_new(human, &data->str);
     g_assert(data->ov);
 }

@@ -53,7 +50,6 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
                                     const void *unused)
 {
     visit_free(data->ov);
-    data->sov = NULL;
     data->ov = NULL;
     g_free(data->str);
     data->str = NULL;
@@ -61,7 +57,7 @@ static void visitor_output_teardown(TestOutputVisitorData *data,

 static char *visitor_get(TestOutputVisitorData *data)
 {
-    data->str = string_output_get_string(data->sov);
+    visit_complete(data->ov, &data->str);
     g_assert(data->str);
     return data->str;
 }
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 63027e1..4da4b8e 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1013,7 +1013,8 @@ static PrimitiveType pt_values[] = {
 /* visitor-specific op implementations */

 typedef struct QmpSerializeData {
-    QmpOutputVisitor *qov;
+    Visitor *qov;
+    QObject *obj;
     Visitor *qiv;
 } QmpSerializeData;

@@ -1022,8 +1023,8 @@ static void qmp_serialize(void *native_in, void **datap,
 {
     QmpSerializeData *d = g_malloc0(sizeof(*d));

-    d->qov = qmp_output_visitor_new();
-    visit(qmp_output_get_visitor(d->qov), &native_in, errp);
+    d->qov = qmp_output_visitor_new(&d->obj);
+    visit(d->qov, &native_in, errp);
     *datap = d;
 }

@@ -1034,7 +1035,8 @@ static void qmp_deserialize(void **native_out, void *datap,
     QString *output_json;
     QObject *obj_orig, *obj;

-    obj_orig = qmp_output_get_qobject(d->qov);
+    visit_complete(d->qov, &d->obj);
+    obj_orig = d->obj;
     output_json = qobject_to_json(obj_orig);
     obj = qobject_from_json(qstring_get_str(output_json));

@@ -1048,7 +1050,7 @@ static void qmp_deserialize(void **native_out, void *datap,
 static void qmp_cleanup(void *datap)
 {
     QmpSerializeData *d = datap;
-    visit_free(qmp_output_get_visitor(d->qov));
+    visit_free(d->qov);
     visit_free(d->qiv);

     g_free(d);
@@ -1056,7 +1058,7 @@ static void qmp_cleanup(void *datap)

 typedef struct StringSerializeData {
     char *string;
-    StringOutputVisitor *sov;
+    Visitor *sov;
     Visitor *siv;
 } StringSerializeData;

@@ -1065,8 +1067,8 @@ static void string_serialize(void *native_in, void **datap,
 {
     StringSerializeData *d = g_malloc0(sizeof(*d));

-    d->sov = string_output_visitor_new(false);
-    visit(string_output_get_visitor(d->sov), &native_in, errp);
+    d->sov = string_output_visitor_new(false, &d->string);
+    visit(d->sov, &native_in, errp);
     *datap = d;
 }

@@ -1075,7 +1077,7 @@ static void string_deserialize(void **native_out, void *datap,
 {
     StringSerializeData *d = datap;

-    d->string = string_output_get_string(d->sov);
+    visit_complete(d->sov, &d->string);
     d->siv = string_input_visitor_new(d->string);
     visit(d->siv, native_out, errp);
 }
@@ -1084,7 +1086,7 @@ static void string_cleanup(void *datap)
 {
     StringSerializeData *d = datap;

-    visit_free(string_output_get_visitor(d->sov));
+    visit_free(d->sov);
     visit_free(d->siv);
     g_free(d->string);
     g_free(d);
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index a34cb33..7175743 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1129,16 +1129,14 @@ SocketAddress *socket_remote_address(int fd, Error **errp)
 void qapi_copy_SocketAddress(SocketAddress **p_dest,
                              SocketAddress *src)
 {
-    QmpOutputVisitor *qov;
     Visitor *ov, *iv;
     QObject *obj;

     *p_dest = NULL;

-    qov = qmp_output_visitor_new();
-    ov = qmp_output_get_visitor(qov);
+    ov = qmp_output_visitor_new(&obj);
     visit_type_SocketAddress(ov, NULL, &src, &error_abort);
-    obj = qmp_output_get_qobject(qov);
+    visit_complete(ov, &obj);
     visit_free(ov);
     if (!obj) {
         return;
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (11 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function Eric Blake
@ 2016-05-19  4:40 ` Eric Blake
  2016-06-02 13:43   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 14/28] sockets: Use new QAPI cloning Eric Blake
                   ` (18 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:40 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We have a couple places in the code base that want to deep-clone
one QAPI object into another, and they were resorting to serializing
the struct out to QObject then reparsing it.  A much more efficient
version can be done by adding a new clone visitor.

Since cloning is still relatively uncommon, expose the use of the
new visitor via a QAPI_CLONE() macro that takes care of type-punning
the underlying function pointer, rather than generating lots of
unused functions for types that won't be cloned.  And yes, we're
relying on the compiler treating all pointers equally, even though
a strict C program cannot portably do so - but we're not the first
one in the qemu code base to expect it to work (hello, glib!).

Note that we can only clone objects (including alternates) and lists,
not built-ins or enums.  The visitor core hides integer width from
the actual visitor (since commit 04e070d), and as long as that's the
case, we can't clone top-level integers.  Then again, those can
always be cloned by direct copy, since they are not objects with
deep pointers, so it's no real loss.  And restricting cloning to
just objects and lists is cleaner than restricting it to non-integers.
As such, I documented that the clone visitor is for direct use only
by code internal to QAPI.

Scalars not at the root of the clone copy just fine, by virtue of a
g_memdup() each time we push another struct onto the stack.

Cloning an 'any' type could be possible by incrementing the QObject
refcnt, but it's not obvious whether that is better than implementing
a QObject deep clone.  So for now, we document it as unsupported,
and intentionally omit the .type_any() callback to let a developer
know their usage needs implementation.

The choice of adding a fourth visitor type deserves some explanation.
On the surface, the clone visitor is mostly an input visitor (it
takes arbitrary input - in this case, another QAPI object - and
creates a new QAPI object during the course of the visit).  But
ever since commit da72ab0 consolidated enum visits based on the
visitor type, using VISITOR_INPUT would cause us to run
visit_type_str(), even though for cloning there is nothing to do
(we just copy the enum value across, without regards to its mapping
to strings).   Also, since our input happens to be a QAPI object,
we can also satisfy the internal checks for VISITOR_OUTPUT.  So in
the end, I settled with a new VISITOR_CLONE, and chose its value
such that many internal checks can use 'v->type & mask', sticking
to 'v->type == value' where the difference matters.

Add testsuite coverage for several different clone situations, to
ensure that the code is working.  I also tested that valgrind was
happy with the test.

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

---
v4: hoist earlier in series, drop wasted generated functions and
replace it with QAPI_CLONE() macro, drop 'any' support, tweak
core assertion checking, internal improve commit message
v3: new patch
---
 include/qapi/visitor.h       |  33 ++++---
 include/qapi/visitor-impl.h  |  12 +--
 include/qapi/clone-visitor.h |  37 ++++++++
 qapi/qapi-visit-core.c       |  18 ++--
 qapi/qapi-clone-visitor.c    | 178 +++++++++++++++++++++++++++++++++++++
 tests/test-clone-visitor.c   | 207 +++++++++++++++++++++++++++++++++++++++++++
 qapi/Makefile.objs           |   2 +-
 tests/.gitignore             |   1 +
 tests/Makefile               |   4 +
 9 files changed, 466 insertions(+), 26 deletions(-)
 create mode 100644 include/qapi/clone-visitor.h
 create mode 100644 qapi/qapi-clone-visitor.c
 create mode 100644 tests/test-clone-visitor.c

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index b3bd97c..3f46921 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -24,15 +24,16 @@
  * 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,
+ * There are four 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
+ * a completed QAPI graph and generate an external representation, the
+ * dealloc visitor can take a QAPI graph (possibly partially
+ * constructed) and recursively free its resources, and the clone
+ * visitor performs a deep clone of one QAPI object to another.  While
+ * the dealloc and QMP input/output visitors are general, the string,
+ * QemuOpts, and clone 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.
@@ -102,11 +103,19 @@
  *
  * 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:
+ * where behaves like free() in that @obj may be NULL.  Such objects
+ * may also be used with the following macro, provided alongside the
+ * clone visitor:
+ *
+ * Type *QAPI_CLONE(Type, src);
+ *
+ * in order to perform a deep clone of @src.  Because of the generated
+ * qapi_free functions and the QAPI_CLONE() macro, the clone and
+ * dealloc visitor should not be used directly outside of QAPI 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);
  *
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 16e0b86..29fac2b 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -27,14 +27,16 @@
  */

 /*
- * There are three classes of visitors; setting the class determines
+ * There are four classes of visitors; setting the class determines
  * how QAPI enums are visited, as well as what additional restrictions
- * can be asserted.
+ * can be asserted.  The values are intentionally chosen so as to
+ * permit some assertions based on whether a given bit is set.
  */
 typedef enum VisitorType {
-    VISITOR_INPUT,
-    VISITOR_OUTPUT,
-    VISITOR_DEALLOC,
+    VISITOR_INPUT = 1,
+    VISITOR_OUTPUT = 2,
+    VISITOR_CLONE = 3,
+    VISITOR_DEALLOC = 4,
 } VisitorType;

 struct Visitor
diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h
new file mode 100644
index 0000000..16ceff5
--- /dev/null
+++ b/include/qapi/clone-visitor.h
@@ -0,0 +1,37 @@
+/*
+ * Clone Visitor
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_CLONE_VISITOR_H
+#define QAPI_CLONE_VISITOR_H
+
+#include "qapi/visitor.h"
+
+/*
+ * The clone visitor is for direct use only by the QAPI_CLONE() macro;
+ * it requires that the root visit occur on an object, list, or
+ * alternate, and is not usable directly on built-in QAPI types.
+ */
+typedef struct QapiCloneVisitor QapiCloneVisitor;
+
+void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
+                                                     void **, Error **));
+
+/*
+ * Deep-clone QAPI object @src of the given @type, and return the result.
+ *
+ * Not usable on QAPI scalars (integers, strings, enums), nor on a
+ * QAPI object that references the 'any' type.  Safe when @src is NULL.
+ */
+#define QAPI_CLONE(type, src)                                           \
+    ((type *)qapi_clone(src,                                            \
+                        (void (*)(Visitor *, const char *, void**,      \
+                                  Error **))visit_type_ ## type))
+
+#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 279ea8e..c5bdca2 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -44,10 +44,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,

     if (obj) {
         assert(size);
-        assert(v->type != VISITOR_OUTPUT || *obj);
+        assert(!(v->type & VISITOR_OUTPUT) || *obj);
     }
     v->start_struct(v, name, obj, size, &err);
-    if (obj && v->type == VISITOR_INPUT) {
+    if (obj && (v->type & VISITOR_INPUT)) {
         assert(!err != !*obj);
     }
     error_propagate(errp, err);
@@ -72,7 +72,7 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list,

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

     assert(obj && size >= sizeof(GenericAlternate));
-    assert(v->type != VISITOR_OUTPUT || *obj);
+    assert(!(v->type & VISITOR_OUTPUT) || *obj);
     if (v->start_alternate) {
         v->start_alternate(v, name, obj, size, promote_int, &err);
     }
-    if (v->type == VISITOR_INPUT) {
+    if (v->type & VISITOR_INPUT) {
         assert(v->start_alternate && !err != !*obj);
     }
     error_propagate(errp, err);
@@ -252,9 +252,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
     assert(obj);
     /* TODO: Fix callers to not pass NULL when they mean "", so that we
      * can enable:
-    assert(v->type != VISITOR_OUTPUT || *obj);
+    assert(!(v->type & VISITOR_OUTPUT) || *obj);
      */
     v->type_str(v, name, obj, &err);
+    /* Likewise, use of NULL means we can't do (v->type & VISITOR_INPUT) */
     if (v->type == VISITOR_INPUT) {
         assert(!err != !*obj);
     }
@@ -273,9 +274,9 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
     Error *err = NULL;

     assert(obj);
-    assert(v->type != VISITOR_OUTPUT || *obj);
+    assert(!(v->type & VISITOR_OUTPUT) || *obj);
     v->type_any(v, name, obj, &err);
-    if (v->type == VISITOR_INPUT) {
+    if (v->type & VISITOR_INPUT) {
         assert(!err != !*obj);
     }
     error_propagate(errp, err);
@@ -342,4 +343,5 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
     } else if (v->type == VISITOR_OUTPUT) {
         output_type_enum(v, name, obj, strings, errp);
     }
+    /* dealloc and clone visitors have nothing to do */
 }
diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c
new file mode 100644
index 0000000..a24a258
--- /dev/null
+++ b/qapi/qapi-clone-visitor.c
@@ -0,0 +1,178 @@
+/*
+ * Copy one QAPI object to another
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/error.h"
+
+struct QapiCloneVisitor {
+    Visitor visitor;
+    const void *root; /* Must be object, alternate, or list */
+    size_t depth;
+};
+
+static QapiCloneVisitor *to_qcv(Visitor *v)
+{
+    return container_of(v, QapiCloneVisitor, visitor);
+}
+
+static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj,
+                                    size_t size, Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    if (!obj) {
+        /* Only possible when visiting an alternate's object
+         * branch. Nothing to do here, since the earlier
+         * visit_start_alternate() already copied memory. */
+        assert(qcv->depth);
+        return;
+    }
+
+    *obj = g_memdup(qcv->depth ? *obj : qcv->root, size);
+    qcv->depth++;
+}
+
+static void qapi_clone_end(Visitor *v, void **obj)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    if (obj) {
+        qcv->depth--;
+    }
+}
+
+static void qapi_clone_start_list(Visitor *v, const char *name,
+                                  GenericList **listp, size_t size,
+                                  Error **errp)
+{
+    qapi_clone_start_struct(v, name, (void **)listp, size, errp);
+}
+
+static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail,
+                                         size_t size)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Unshare the tail of the list cloned by g_memdup */
+    tail->next = g_memdup(tail->next, size);
+    return tail->next;
+}
+
+static void qapi_clone_start_alternate(Visitor *v, const char *name,
+                                       GenericAlternate **obj, size_t size,
+                                       bool promote_int, Error **errp)
+{
+    qapi_clone_start_struct(v, name, (void **)obj, size, errp);
+}
+
+static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj,
+                                   Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup */
+}
+
+static void qapi_clone_type_uint64(Visitor *v, const char *name,
+                                    uint64_t *obj, Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup */
+}
+
+static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj,
+                                  Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup */
+}
+
+static void qapi_clone_type_str(Visitor *v, const char *name, char **obj,
+                                 Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Pointer was already cloned by g_memdup; create fresh copy */
+    *obj = g_strdup(*obj);
+}
+
+static void qapi_clone_type_number(Visitor *v, const char *name, double *obj,
+                                    Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Value was already cloned by g_memdup */
+}
+
+static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp)
+{
+    QapiCloneVisitor *qcv = to_qcv(v);
+
+    assert(qcv->depth);
+    /* Nothing to do */
+}
+
+static void qapi_clone_free(Visitor *v)
+{
+    g_free(v);
+}
+
+static Visitor *qapi_clone_visitor_new(const void *src)
+{
+    QapiCloneVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+    v->root = src;
+
+    v->visitor.type = VISITOR_CLONE;
+    v->visitor.start_struct = qapi_clone_start_struct;
+    v->visitor.end_struct = qapi_clone_end;
+    v->visitor.start_list = qapi_clone_start_list;
+    v->visitor.next_list = qapi_clone_next_list;
+    v->visitor.end_list = qapi_clone_end;
+    v->visitor.start_alternate = qapi_clone_start_alternate;
+    v->visitor.end_alternate = qapi_clone_end;
+    v->visitor.type_int64 = qapi_clone_type_int64;
+    v->visitor.type_uint64 = qapi_clone_type_uint64;
+    v->visitor.type_bool = qapi_clone_type_bool;
+    v->visitor.type_str = qapi_clone_type_str;
+    v->visitor.type_number = qapi_clone_type_number;
+    v->visitor.type_null = qapi_clone_type_null;
+    v->visitor.free = qapi_clone_free;
+
+    return &v->visitor;
+}
+
+void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
+                                                     void **, Error **))
+{
+    Visitor *v;
+    void *dst;
+
+    if (!src) {
+        return NULL;
+    }
+
+    v = qapi_clone_visitor_new(src);
+    visit_type(v, NULL, &dst, &error_abort);
+    visit_free(v);
+    return dst;
+}
diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c
new file mode 100644
index 0000000..ab3fead
--- /dev/null
+++ b/tests/test-clone-visitor.c
@@ -0,0 +1,207 @@
+/*
+ * QAPI Clone Visitor unit-tests.
+ *
+ * Copyright (C) 2016 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/clone-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qapi/qmp/types.h"
+
+static void test_clone_struct(void)
+{
+    UserDefOne *src, *dst;
+
+    src = g_new0(UserDefOne, 1);
+    src->integer = 42;
+    src->string = g_strdup("Hello");
+    src->has_enum1 = false;
+    src->enum1 = ENUM_ONE_VALUE2;
+
+    dst = QAPI_CLONE(UserDefOne, src);
+    g_assert(dst);
+    g_assert_cmpint(dst->integer, ==, 42);
+    g_assert(dst->string != src->string);
+    g_assert_cmpstr(dst->string, ==, "Hello");
+    g_assert_cmpint(dst->has_enum1, ==, false);
+    /* Our implementation does this, but it is not required:
+    g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2);
+    */
+
+    qapi_free_UserDefOne(src);
+    qapi_free_UserDefOne(dst);
+}
+
+static void test_clone_alternate(void)
+{
+    AltStrBool *b_src, *s_src, *b_dst, *s_dst;
+
+    b_src = g_new0(AltStrBool, 1);
+    b_src->type = QTYPE_QBOOL;
+    b_src->u.b = true;
+    s_src = g_new0(AltStrBool, 1);
+    s_src->type = QTYPE_QSTRING;
+    s_src->u.s = g_strdup("World");
+
+    b_dst = QAPI_CLONE(AltStrBool, b_src);
+    g_assert(b_dst);
+    g_assert_cmpint(b_dst->type, ==, b_src->type);
+    g_assert_cmpint(b_dst->u.b, ==, b_src->u.b);
+    s_dst = QAPI_CLONE(AltStrBool, s_src);
+    g_assert(s_dst);
+    g_assert_cmpint(s_dst->type, ==, s_src->type);
+    g_assert_cmpstr(s_dst->u.s, ==, s_src->u.s);
+    g_assert(s_dst->u.s != s_src->u.s);
+
+    qapi_free_AltStrBool(b_src);
+    qapi_free_AltStrBool(s_src);
+    qapi_free_AltStrBool(b_dst);
+    qapi_free_AltStrBool(s_dst);
+}
+
+static void test_clone_native_list(void)
+{
+    uint8List *src, *dst;
+    uint8List *tmp = NULL;
+    int i;
+
+    /* Build list in reverse */
+    for (i = 10; i; i--) {
+        src = g_new0(uint8List, 1);
+        src->next = tmp;
+        src->value = i;
+        tmp = src;
+    }
+
+    dst = QAPI_CLONE(uint8List, src);
+    for (tmp = dst, i = 1; i <= 10; i++) {
+        g_assert(tmp);
+        g_assert_cmpint(tmp->value, ==, i);
+        tmp = tmp->next;
+    }
+    g_assert(!tmp);
+
+    qapi_free_uint8List(src);
+    qapi_free_uint8List(dst);
+}
+
+static void test_clone_empty(void)
+{
+    Empty2 *src, *dst;
+
+    src = g_new0(Empty2, 1);
+    dst = QAPI_CLONE(Empty2, src);
+    g_assert(dst);
+    qapi_free_Empty2(src);
+    qapi_free_Empty2(dst);
+}
+
+static void test_clone_complex1(void)
+{
+    UserDefNativeListUnion *src, *dst;
+
+    src = g_new0(UserDefNativeListUnion, 1);
+    src->type = USER_DEF_NATIVE_LIST_UNION_KIND_STRING;
+
+    dst = QAPI_CLONE(UserDefNativeListUnion, src);
+    g_assert(dst);
+    g_assert_cmpint(dst->type, ==, src->type);
+    g_assert(!dst->u.string.data);
+
+    qapi_free_UserDefNativeListUnion(src);
+    qapi_free_UserDefNativeListUnion(dst);
+}
+
+static void test_clone_complex2(void)
+{
+    WrapAlternate *src, *dst;
+
+    src = g_new0(WrapAlternate, 1);
+    src->alt = g_new(UserDefAlternate, 1);
+    src->alt->type = QTYPE_QDICT;
+    src->alt->u.udfu.integer = 42;
+    /* FIXME: As long as QMP output visitor allows NULL in place of ""
+     * for strings, the clone visitor must do likewise. */
+    src->alt->u.udfu.string = NULL;
+    src->alt->u.udfu.enum1 = ENUM_ONE_VALUE3;
+    src->alt->u.udfu.u.value3.intb = 99;
+    src->alt->u.udfu.u.value3.has_a_b = true;
+    src->alt->u.udfu.u.value3.a_b = true;
+
+    dst = QAPI_CLONE(WrapAlternate, src);
+    g_assert(dst);
+    g_assert(dst->alt);
+    g_assert_cmpint(dst->alt->type, ==, QTYPE_QDICT);
+    g_assert_cmpint(dst->alt->u.udfu.integer, ==, 42);
+    g_assert_cmpstr(dst->alt->u.udfu.string, ==, NULL);
+    g_assert_cmpint(dst->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE3);
+    g_assert_cmpint(dst->alt->u.udfu.u.value3.intb, ==, 99);
+    g_assert_cmpint(dst->alt->u.udfu.u.value3.has_a_b, ==, true);
+    g_assert_cmpint(dst->alt->u.udfu.u.value3.a_b, ==, true);
+
+    qapi_free_WrapAlternate(src);
+    qapi_free_WrapAlternate(dst);
+}
+
+static void test_clone_complex3(void)
+{
+    __org_qemu_x_Struct2 *src, *dst;
+    __org_qemu_x_Union1List *tmp;
+
+    src = g_new0(__org_qemu_x_Struct2, 1);
+    tmp = src->array = g_new0(__org_qemu_x_Union1List, 1);
+    tmp->value = g_new0(__org_qemu_x_Union1, 1);
+    tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    tmp->value->u.__org_qemu_x_branch.data = g_strdup("one");
+    tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1);
+    tmp->value = g_new0(__org_qemu_x_Union1, 1);
+    tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    tmp->value->u.__org_qemu_x_branch.data = g_strdup("two");
+    tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1);
+    tmp->value = g_new0(__org_qemu_x_Union1, 1);
+    tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+    tmp->value->u.__org_qemu_x_branch.data = g_strdup("three");
+
+    dst = QAPI_CLONE(__org_qemu_x_Struct2, src);
+    g_assert(dst);
+    tmp = dst->array;
+    g_assert(tmp);
+    g_assert(tmp->value);
+    g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "one");
+    tmp = tmp->next;
+    g_assert(tmp);
+    g_assert(tmp->value);
+    g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "two");
+    tmp = tmp->next;
+    g_assert(tmp);
+    g_assert(tmp->value);
+    g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "three");
+    tmp = tmp->next;
+    g_assert(!tmp);
+
+    qapi_free___org_qemu_x_Struct2(src);
+    qapi_free___org_qemu_x_Struct2(dst);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/visitor/clone/struct", test_clone_struct);
+    g_test_add_func("/visitor/clone/alternate", test_clone_alternate);
+    g_test_add_func("/visitor/clone/native_list", test_clone_native_list);
+    g_test_add_func("/visitor/clone/empty", test_clone_empty);
+    g_test_add_func("/visitor/clone/complex1", test_clone_complex1);
+    g_test_add_func("/visitor/clone/complex2", test_clone_complex2);
+    g_test_add_func("/visitor/clone/complex3", test_clone_complex3);
+
+    return g_test_run();
+}
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 2278970..7ea4aeb 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,6 +1,6 @@
 util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
-util-obj-y += opts-visitor.o
+util-obj-y += opts-visitor.o qapi-clone-visitor.o
 util-obj-y += qmp-event.o
 util-obj-y += qapi-util.o
diff --git a/tests/.gitignore b/tests/.gitignore
index 2f8c7ea..346d75d 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -12,6 +12,7 @@ test-aio
 test-base64
 test-bitops
 test-blockjob-txn
+test-clone-visitor
 test-coroutine
 test-crypto-afsplit
 test-crypto-block
diff --git a/tests/Makefile b/tests/Makefile
index 1c5cb34..d596493 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -22,6 +22,8 @@ check-unit-y += tests/check-qobject-json$(EXESUF)
 gcov-files-check-qobject-json-y = qobject/qobject-json.c
 check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
 gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
+check-unit-y += tests/test-clone-visitor$(EXESUF)
+gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c
 check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
 gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c
 check-unit-y += tests/test-qmp-input-strict$(EXESUF)
@@ -388,6 +390,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 	tests/check-qobject-json.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-clone-visitor.o \
 	tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
 	tests/test-qmp-commands.o tests/test-visitor-serialization.o \
 	tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
@@ -478,6 +481,7 @@ tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
 tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y)
+tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y)
 tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 14/28] sockets: Use new QAPI cloning
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (12 preceding siblings ...)
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 15/28] replay: " Eric Blake
                   ` (17 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Daniel P. Berrange, Michael Roth, Gerd Hoffmann, Paolo Bonzini

Rather than rolling our own clone via an expensive conversion
in and back out of QObject, use the new clone visitor.

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

---
v4: completely drop qapi_copy_SocketAddress(), rebase to earlier changes
v3: new patch
---
 include/io/task.h            |  2 +-
 include/qapi/clone-visitor.h |  2 ++
 include/qemu/sockets.h       |  4 ----
 io/channel-socket.c          |  9 +++++----
 qemu-char.c                  |  5 ++---
 util/qemu-sockets.c          | 23 -----------------------
 6 files changed, 10 insertions(+), 35 deletions(-)

diff --git a/include/io/task.h b/include/io/task.h
index 2e69d8a..cf31d57 100644
--- a/include/io/task.h
+++ b/include/io/task.h
@@ -159,7 +159,7 @@ typedef int (*QIOTaskWorker)(QIOTask *task,
  *      QIOTask *task;
  *      SocketAddress *addrCopy;
  *
- *      qapi_copy_SocketAddress(&addrCopy, addr);
+ *      addrCopy = QAPI_CLONE(SocketAddress, addr);
  *      task = qio_task_new(OBJECT(obj), func, opaque, notify);
  *
  *      qio_task_run_in_thread(task, myobject_listen_worker,
diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h
index 16ceff5..b16177e 100644
--- a/include/qapi/clone-visitor.h
+++ b/include/qapi/clone-visitor.h
@@ -11,7 +11,9 @@
 #ifndef QAPI_CLONE_VISITOR_H
 #define QAPI_CLONE_VISITOR_H

+#include "qemu/typedefs.h"
 #include "qapi/visitor.h"
+#include "qapi-visit.h"

 /*
  * The clone visitor is for direct use only by the QAPI_CLONE() macro;
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 1bd9218..3e3445f 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -106,8 +106,4 @@ SocketAddress *socket_local_address(int fd, Error **errp);
  */
 SocketAddress *socket_remote_address(int fd, Error **errp);

-
-void qapi_copy_SocketAddress(SocketAddress **p_dest,
-                             SocketAddress *src);
-
 #endif /* QEMU_SOCKET_H */
diff --git a/io/channel-socket.c b/io/channel-socket.c
index ca8bc20..27eee07 100644
--- a/io/channel-socket.c
+++ b/io/channel-socket.c
@@ -23,6 +23,7 @@
 #include "io/channel-socket.h"
 #include "io/channel-watch.h"
 #include "trace.h"
+#include "qapi/clone-visitor.h"

 #define SOCKET_MAX_FDS 16

@@ -182,7 +183,7 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
         OBJECT(ioc), callback, opaque, destroy);
     SocketAddress *addrCopy;

-    qapi_copy_SocketAddress(&addrCopy, addr);
+    addrCopy = QAPI_CLONE(SocketAddress, addr);

     /* socket_connect() does a non-blocking connect(), but it
      * still blocks in DNS lookups, so we must use a thread */
@@ -244,7 +245,7 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
         OBJECT(ioc), callback, opaque, destroy);
     SocketAddress *addrCopy;

-    qapi_copy_SocketAddress(&addrCopy, addr);
+    addrCopy = QAPI_CLONE(SocketAddress, addr);

     /* socket_listen() blocks in DNS lookups, so we must use a thread */
     trace_qio_channel_socket_listen_async(ioc, addr);
@@ -324,8 +325,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
     struct QIOChannelSocketDGramWorkerData *data = g_new0(
         struct QIOChannelSocketDGramWorkerData, 1);

-    qapi_copy_SocketAddress(&data->localAddr, localAddr);
-    qapi_copy_SocketAddress(&data->remoteAddr, remoteAddr);
+    data->localAddr = QAPI_CLONE(SocketAddress, localAddr);
+    data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr);

     trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr);
     qio_task_run_in_thread(task,
diff --git a/qemu-char.c b/qemu-char.c
index b597ee1..0f0587e 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -32,8 +32,7 @@
 #include "sysemu/char.h"
 #include "hw/usb.h"
 #include "qmp-commands.h"
-#include "qapi/qmp-input-visitor.h"
-#include "qapi/qmp-output-visitor.h"
+#include "qapi/clone-visitor.h"
 #include "qapi-visit.h"
 #include "qemu/base64.h"
 #include "io/channel-socket.h"
@@ -4399,7 +4398,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
         }
     }

-    qapi_copy_SocketAddress(&s->addr, sock->addr);
+    s->addr = QAPI_CLONE(SocketAddress, sock->addr);

     chr->opaque = s;
     chr->chr_write = tcp_chr_write;
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 7175743..e9dab1a 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1124,26 +1124,3 @@ SocketAddress *socket_remote_address(int fd, Error **errp)

     return socket_sockaddr_to_address(&ss, sslen, errp);
 }
-
-
-void qapi_copy_SocketAddress(SocketAddress **p_dest,
-                             SocketAddress *src)
-{
-    Visitor *ov, *iv;
-    QObject *obj;
-
-    *p_dest = NULL;
-
-    ov = qmp_output_visitor_new(&obj);
-    visit_type_SocketAddress(ov, NULL, &src, &error_abort);
-    visit_complete(ov, &obj);
-    visit_free(ov);
-    if (!obj) {
-        return;
-    }
-
-    iv = qmp_input_visitor_new(obj, true);
-    visit_type_SocketAddress(iv, NULL, p_dest, &error_abort);
-    visit_free(iv);
-    qobject_decref(obj);
-}
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 15/28] replay: Use new QAPI cloning
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (13 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 14/28] sockets: Use new QAPI cloning Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping Eric Blake
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

Rather than rolling our own clone via an expensive conversion
in and back out of QObject, use the new clone visitor.

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

---
v4: rebase to earlier changes
v3: new patch
---
 replay/replay-input.c | 30 +++---------------------------
 1 file changed, 3 insertions(+), 27 deletions(-)

diff --git a/replay/replay-input.c b/replay/replay-input.c
index 296399c..bd93554 100644
--- a/replay/replay-input.c
+++ b/replay/replay-input.c
@@ -16,31 +16,7 @@
 #include "replay-internal.h"
 #include "qemu/notify.h"
 #include "ui/input.h"
-#include "qapi/qmp-output-visitor.h"
-#include "qapi/qmp-input-visitor.h"
-#include "qapi-visit.h"
-
-static InputEvent *qapi_clone_InputEvent(InputEvent *src)
-{
-    Visitor *ov, *iv;
-    QObject *obj;
-    InputEvent *dst = NULL;
-
-    ov = qmp_output_visitor_new(&obj);
-    visit_type_InputEvent(ov, NULL, &src, &error_abort);
-    visit_complete(ov, &obj);
-    visit_free(ov);
-    if (!obj) {
-        return NULL;
-    }
-
-    iv = qmp_input_visitor_new(obj, true);
-    visit_type_InputEvent(iv, NULL, &dst, &error_abort);
-    visit_free(iv);
-    qobject_decref(obj);
-
-    return dst;
-}
+#include "qapi/clone-visitor.h"

 void replay_save_input_event(InputEvent *evt)
 {
@@ -139,7 +115,7 @@ InputEvent *replay_read_input_event(void)
         break;
     }

-    return qapi_clone_InputEvent(&evt);
+    return QAPI_CLONE(InputEvent, &evt);
 }

 void replay_input_event(QemuConsole *src, InputEvent *evt)
@@ -147,7 +123,7 @@ void replay_input_event(QemuConsole *src, InputEvent *evt)
     if (replay_mode == REPLAY_MODE_PLAY) {
         /* Nothing */
     } else if (replay_mode == REPLAY_MODE_RECORD) {
-        replay_add_input_event(qapi_clone_InputEvent(evt));
+        replay_add_input_event(QAPI_CLONE(InputEvent, evt));
     } else {
         qemu_input_event_send_impl(src, evt);
     }
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (14 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 15/28] replay: " Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-02 14:53   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting Eric Blake
                   ` (15 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino, Juan Quintela, Amit Shah

Pull out a new qstring_append_json_string() helper, so that all
JSON output producers can use the same output escaping rules.

While it appears that vmstate's use of the simpler qjson.c
formatter is not currently encountering any string that needs
escapes to be valid JSON, it is better to be safe than sorry,
and this substitution is not adding any potential asserts
during migration.

Adding a return value will let callers that care diagnose the
situation where an attempt was made to output invalid JSON (we
use substitute characters, and in fact guarantee that our
output is ASCII via escapes even if the input was UTF-8).  None
of the current callers care, but a future patch wants to make
it possible to turn this situation into an error.

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

---
v4: keep helper in qobject-json.[ch], tweak variable name and
for loop iterator, add return value, drop R-b
v3: rebase to include cleanups in master
v2: no change
---
 include/qapi/qmp/qobject-json.h |   2 +
 migration/qjson.c               |  10 ++--
 qobject/qobject-json.c          | 125 ++++++++++++++++++++++------------------
 3 files changed, 75 insertions(+), 62 deletions(-)

diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index 02b1f2c..aa8ddd7 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -24,4 +24,6 @@ QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0);
 QString *qobject_to_json(const QObject *obj);
 QString *qobject_to_json_pretty(const QObject *obj);

+int qstring_append_json_string(QString *qstring, const char *str);
+
 #endif /* QJSON_H */
diff --git a/migration/qjson.c b/migration/qjson.c
index 5cae55a..b053eff 100644
--- a/migration/qjson.c
+++ b/migration/qjson.c
@@ -26,6 +26,7 @@
 #include "qemu/osdep.h"
 #include "qapi/qmp/qstring.h"
 #include "migration/qjson.h"
+#include "qapi/qmp/qobject-json.h"

 struct QJSON {
     QString *str;
@@ -42,9 +43,8 @@ static void json_emit_element(QJSON *json, const char *name)
     }

     if (name) {
-        qstring_append(json->str, "\"");
-        qstring_append(json->str, name);
-        qstring_append(json->str, "\" : ");
+        qstring_append_json_string(json->str, name);
+        qstring_append(json->str, " : ");
     }
 }

@@ -83,9 +83,7 @@ void json_prop_int(QJSON *json, const char *name, int64_t val)
 void json_prop_str(QJSON *json, const char *name, const char *str)
 {
     json_emit_element(json, name);
-    qstring_append_chr(json->str, '"');
-    qstring_append(json->str, str);
-    qstring_append_chr(json->str, '"');
+    qstring_append_json_string(json->str, str);
 }

 const char *qjson_get_str(QJSON *json)
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 24e7d80..bc3634f 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -81,7 +81,6 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent);
 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
     ToJsonIterState *s = opaque;
-    QString *qkey;
     int j;

     if (s->count) {
@@ -94,9 +93,7 @@ static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
             qstring_append(s->str, "    ");
     }

-    qkey = qstring_from_str(key);
-    to_json(QOBJECT(qkey), s->str, s->pretty, s->indent);
-    QDECREF(qkey);
+    qstring_append_json_string(s->str, key);

     qstring_append(s->str, ": ");
     to_json(obj, s->str, s->pretty, s->indent);
@@ -138,58 +135,9 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
     }
     case QTYPE_QSTRING: {
         QString *val = qobject_to_qstring(obj);
-        const char *ptr;
-        int cp;
-        char buf[16];
-        char *end;
-
-        ptr = qstring_get_str(val);
-        qstring_append(str, "\"");
-
-        for (; *ptr; ptr = end) {
-            cp = mod_utf8_codepoint(ptr, 6, &end);
-            switch (cp) {
-            case '\"':
-                qstring_append(str, "\\\"");
-                break;
-            case '\\':
-                qstring_append(str, "\\\\");
-                break;
-            case '\b':
-                qstring_append(str, "\\b");
-                break;
-            case '\f':
-                qstring_append(str, "\\f");
-                break;
-            case '\n':
-                qstring_append(str, "\\n");
-                break;
-            case '\r':
-                qstring_append(str, "\\r");
-                break;
-            case '\t':
-                qstring_append(str, "\\t");
-                break;
-            default:
-                if (cp < 0) {
-                    cp = 0xFFFD; /* replacement character */
-                }
-                if (cp > 0xFFFF) {
-                    /* beyond BMP; need a surrogate pair */
-                    snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
-                             0xD800 + ((cp - 0x10000) >> 10),
-                             0xDC00 + ((cp - 0x10000) & 0x3FF));
-                } else if (cp < 0x20 || cp >= 0x7F) {
-                    snprintf(buf, sizeof(buf), "\\u%04X", cp);
-                } else {
-                    buf[0] = cp;
-                    buf[1] = 0;
-                }
-                qstring_append(str, buf);
-            }
-        };
-
-        qstring_append(str, "\"");
+        /* FIXME: no way inform user if we modified the string to
+         * avoid encoding errors */
+        qstring_append_json_string(str, qstring_get_str(val));
         break;
     }
     case QTYPE_QDICT: {
@@ -290,3 +238,68 @@ QString *qobject_to_json_pretty(const QObject *obj)

     return str;
 }
+
+/**
+ * Append a JSON string to @qstring that encodes the C string @str.
+ *
+ * The JSON string is enclosed in double quotes and has funny
+ * characters escaped.  Returns -1 if encoding errors were replaced,
+ * or 0 if the input was already valid UTF-8.
+ */
+int qstring_append_json_string(QString *qstring, const char *str)
+{
+    const char *ptr;
+    int cp;
+    char buf[16];
+    char *end;
+    int result = 0;
+
+    qstring_append(qstring, "\"");
+
+    for (ptr = str; *ptr; ptr = end) {
+        cp = mod_utf8_codepoint(ptr, 6, &end);
+        switch (cp) {
+        case '\"':
+            qstring_append(qstring, "\\\"");
+            break;
+        case '\\':
+            qstring_append(qstring, "\\\\");
+            break;
+        case '\b':
+            qstring_append(qstring, "\\b");
+            break;
+        case '\f':
+            qstring_append(qstring, "\\f");
+            break;
+        case '\n':
+            qstring_append(qstring, "\\n");
+            break;
+        case '\r':
+            qstring_append(qstring, "\\r");
+            break;
+        case '\t':
+            qstring_append(qstring, "\\t");
+            break;
+        default:
+            if (cp < 0) {
+                cp = 0xFFFD; /* replacement character */
+                result = -1;
+            }
+            if (cp > 0xFFFF) {
+                /* beyond BMP; need a surrogate pair */
+                snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
+                         0xD800 + ((cp - 0x10000) >> 10),
+                         0xDC00 + ((cp - 0x10000) & 0x3FF));
+            } else if (cp < 0x20 || cp >= 0x7F) {
+                snprintf(buf, sizeof(buf), "\\u%04X", cp);
+            } else {
+                buf[0] = cp;
+                buf[1] = 0;
+            }
+            qstring_append(qstring, buf);
+        }
+    };
+
+    qstring_append(qstring, "\"");
+    return result;
+}
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (15 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-02 15:02   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 18/28] qapi: Add qstring_append_printf() Eric Blake
                   ` (14 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino

Pull out a new qstring_append_json_number() helper, so that all
JSON output producers can use a consistent style for printing
floating point without duplicating code (since we are doing more
data massaging than a simple printf format can handle).  (For
now, there is only one client, but later patches will use it.)

Adding a return value will let callers that care diagnose the
situation where an attempt was made to output invalid JSON (which
does not specify Infinity or NaN). None of the current callers
care, but a future patch wants to make it possible to turn this
situation into an error.

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

---
v4: keep helper in qobject-json.[ch], don't use Error **errp for
inf/NaN handling, drop R-b
v3: rebase to latest; even though the patch differs quite a bit
from v2, the changes are due to prior comment changes that are
just moving between files, so R-b kept
v2: minor wording tweaks
---
 include/qapi/qmp/qobject-json.h |  1 +
 qobject/qobject-json.c          | 63 +++++++++++++++++++++++++----------------
 2 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index aa8ddd7..0749e7e 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -25,5 +25,6 @@ QString *qobject_to_json(const QObject *obj);
 QString *qobject_to_json_pretty(const QObject *obj);

 int qstring_append_json_string(QString *qstring, const char *str);
+int qstring_append_json_number(QString *qstring, double number);

 #endif /* QJSON_H */
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index bc3634f..08daeed 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -18,6 +18,7 @@
 #include "qapi/qmp/qobject-json.h"
 #include "qemu/unicode.h"
 #include "qapi/qmp/types.h"
+#include <math.h>

 typedef struct JSONParsingState
 {
@@ -180,30 +181,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
     }
     case QTYPE_QFLOAT: {
         QFloat *val = qobject_to_qfloat(obj);
-        char buffer[1024];
-        int len;
-
-        /* FIXME: snprintf() is locale dependent; but JSON requires
-         * numbers to be formatted as if in the C locale. Dependence
-         * on C locale is a pervasive issue in QEMU. */
-        /* FIXME: This risks printing Inf or NaN, which are not valid
-         * JSON values. */
-        /* FIXME: the default precision of 6 for %f often causes
-         * rounding errors; we should be using DBL_DECIMAL_DIG (17),
-         * and only rounding to a shorter number if the result would
-         * still produce the same floating point value.  */
-        len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val));
-        while (len > 0 && buffer[len - 1] == '0') {
-            len--;
-        }
-
-        if (len && buffer[len - 1] == '.') {
-            buffer[len - 1] = 0;
-        } else {
-            buffer[len] = 0;
-        }
-
-        qstring_append(str, buffer);
+        /* FIXME: no way inform user if we generated invalid JSON */
+        qstring_append_json_number(str, qfloat_get_double(val));
         break;
     }
     case QTYPE_QBOOL: {
@@ -303,3 +282,39 @@ int qstring_append_json_string(QString *qstring, const char *str)
     qstring_append(qstring, "\"");
     return result;
 }
+
+/**
+ * Append a JSON representation of @number to @qstring.
+ *
+ * Returns -1 if the added text is not strict JSON, or 0 if the number
+ * was finite.
+ */
+int qstring_append_json_number(QString *qstring, double number)
+{
+    char buffer[1024];
+    int len;
+
+    /* FIXME: snprintf() is locale dependent; but JSON requires
+     * numbers to be formatted as if in the C locale. Dependence
+     * on C locale is a pervasive issue in QEMU. */
+    /* FIXME: This risks printing Inf or NaN, which are not valid
+     * JSON values. */
+    /* FIXME: the default precision of 6 for %f often causes
+     * rounding errors; we should be using DBL_DECIMAL_DIG (17),
+     * and only rounding to a shorter number if the result would
+     * still produce the same floating point value.  */
+    len = snprintf(buffer, sizeof(buffer), "%f", number);
+    assert(len > 0 && len < sizeof(buffer));
+    while (len > 0 && buffer[len - 1] == '0') {
+        len--;
+    }
+
+    if (len && buffer[len - 1] == '.') {
+        buffer[len - 1] = 0;
+    } else {
+        buffer[len] = 0;
+    }
+
+    qstring_append(qstring, buffer);
+    return isfinite(number) ? 0 : -1;
+}
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 18/28] qapi: Add qstring_append_printf()
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (16 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 19/28] qapi: Use qstring_append_chr() where appropriate Eric Blake
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino, Juan Quintela, Amit Shah

Back in commit 764c1ca (Nov 2009), we added qstring_append_int().
However, it did not see any use until commit 190c882 (Jan 2015).
Furthermore, it has a rather limited use case - to print anything
else, callers still have to format into a temporary buffer, unless
we want to introduce an explosion of new qstring_append_* methods
for each useful type to print.

A much better approach is to add a wrapper that merges printf
behavior onto qstring_append, via the new qstring_append_printf()
(and its vararg counterpart), with a name based on glib's
g_string_append_printf().  In fact, with our wrapper in place, we
no longer need qstring_append_int().

Other immediate uses for the new function include simplifying
two existing clients of qstring_append() on a just-formatted
buffer, and the fact that we can take advantage of printf width
manipulations for more efficient indentation.

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

---
v4: retitle, s/_format/_printf/, drop R-b, move up in series
v3: rebase to master
v2: also simplify qstring_append_json_string(), add assertion that
format is well-formed
---
 include/qapi/qmp/qstring.h |  7 +++++--
 migration/qjson.c          |  2 +-
 qobject/qobject-json.c     | 38 ++++++++++----------------------------
 qobject/qstring.c          | 26 +++++++++++++++++++++-----
 4 files changed, 37 insertions(+), 36 deletions(-)

diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h
index 10076b7..a987f3b 100644
--- a/include/qapi/qmp/qstring.h
+++ b/include/qapi/qmp/qstring.h
@@ -1,7 +1,7 @@
 /*
  * QString Module
  *
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2016 Red Hat Inc.
  *
  * Authors:
  *  Luiz Capitulino <lcapitulino@redhat.com>
@@ -27,9 +27,12 @@ QString *qstring_from_str(const char *str);
 QString *qstring_from_substr(const char *str, int start, int end);
 size_t qstring_get_length(const QString *qstring);
 const char *qstring_get_str(const QString *qstring);
-void qstring_append_int(QString *qstring, int64_t value);
 void qstring_append(QString *qstring, const char *str);
 void qstring_append_chr(QString *qstring, int c);
+void qstring_append_printf(QString *qstring, const char *fmt, ...)
+    GCC_FMT_ATTR(2, 3);
+void qstring_append_vprintf(QString *qstring, const char *fmt, va_list ap)
+    GCC_FMT_ATTR(2, 0);
 QString *qobject_to_qstring(const QObject *obj);
 void qstring_destroy_obj(QObject *obj);

diff --git a/migration/qjson.c b/migration/qjson.c
index b053eff..1bf089d 100644
--- a/migration/qjson.c
+++ b/migration/qjson.c
@@ -77,7 +77,7 @@ void json_end_array(QJSON *json)
 void json_prop_int(QJSON *json, const char *name, int64_t val)
 {
     json_emit_element(json, name);
-    qstring_append_int(json->str, val);
+    qstring_append_printf(json->str, "%" PRId64, val);
 }

 void json_prop_str(QJSON *json, const char *name, const char *str)
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 08daeed..769816d 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -82,16 +82,13 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent);
 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
     ToJsonIterState *s = opaque;
-    int j;

     if (s->count) {
         qstring_append(s->str, s->pretty ? "," : ", ");
     }

     if (s->pretty) {
-        qstring_append(s->str, "\n");
-        for (j = 0 ; j < s->indent ; j++)
-            qstring_append(s->str, "    ");
+        qstring_append_printf(s->str, "\n%*s", 4 * s->indent, "");
     }

     qstring_append_json_string(s->str, key);
@@ -104,16 +101,13 @@ static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 static void to_json_list_iter(QObject *obj, void *opaque)
 {
     ToJsonIterState *s = opaque;
-    int j;

     if (s->count) {
         qstring_append(s->str, s->pretty ? "," : ", ");
     }

     if (s->pretty) {
-        qstring_append(s->str, "\n");
-        for (j = 0 ; j < s->indent ; j++)
-            qstring_append(s->str, "    ");
+        qstring_append_printf(s->str, "\n%*s", 4 * s->indent, "");
     }

     to_json(obj, s->str, s->pretty, s->indent);
@@ -128,10 +122,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         break;
     case QTYPE_QINT: {
         QInt *val = qobject_to_qint(obj);
-        char buffer[1024];
-
-        snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val));
-        qstring_append(str, buffer);
+        qstring_append_printf(str, "%" PRId64, qint_get_int(val));
         break;
     }
     case QTYPE_QSTRING: {
@@ -152,10 +143,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         qstring_append(str, "{");
         qdict_iter(val, to_json_dict_iter, &s);
         if (pretty) {
-            int j;
-            qstring_append(str, "\n");
-            for (j = 0 ; j < indent ; j++)
-                qstring_append(str, "    ");
+            qstring_append_printf(str, "\n%*s", 4 * indent, "");
         }
         qstring_append(str, "}");
         break;
@@ -171,10 +159,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         qstring_append(str, "[");
         qlist_iter(val, (void *)to_json_list_iter, &s);
         if (pretty) {
-            int j;
-            qstring_append(str, "\n");
-            for (j = 0 ; j < indent ; j++)
-                qstring_append(str, "    ");
+            qstring_append_printf(str, "\n%*s", 4 * indent, "");
         }
         qstring_append(str, "]");
         break;
@@ -229,7 +214,6 @@ int qstring_append_json_string(QString *qstring, const char *str)
 {
     const char *ptr;
     int cp;
-    char buf[16];
     char *end;
     int result = 0;

@@ -266,16 +250,14 @@ int qstring_append_json_string(QString *qstring, const char *str)
             }
             if (cp > 0xFFFF) {
                 /* beyond BMP; need a surrogate pair */
-                snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
-                         0xD800 + ((cp - 0x10000) >> 10),
-                         0xDC00 + ((cp - 0x10000) & 0x3FF));
+                qstring_append_printf(qstring, "\\u%04X\\u%04X",
+                                      0xD800 + ((cp - 0x10000) >> 10),
+                                      0xDc00 + ((cp - 0x10000) & 0x3FF));
             } else if (cp < 0x20 || cp >= 0x7F) {
-                snprintf(buf, sizeof(buf), "\\u%04X", cp);
+                qstring_append_printf(qstring, "\\u%04X", cp);
             } else {
-                buf[0] = cp;
-                buf[1] = 0;
+                qstring_append_chr(qstring, cp);
             }
-            qstring_append(qstring, buf);
         }
     };

diff --git a/qobject/qstring.c b/qobject/qstring.c
index 5da7b5f..fbfae27 100644
--- a/qobject/qstring.c
+++ b/qobject/qstring.c
@@ -1,7 +1,7 @@
 /*
  * QString Module
  *
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2016 Red Hat Inc.
  *
  * Authors:
  *  Luiz Capitulino <lcapitulino@redhat.com>
@@ -88,12 +88,28 @@ void qstring_append(QString *qstring, const char *str)
     qstring->string[qstring->length] = 0;
 }

-void qstring_append_int(QString *qstring, int64_t value)
+void qstring_append_printf(QString *qstring, const char *fmt, ...)
 {
-    char num[32];
+    va_list ap;

-    snprintf(num, sizeof(num), "%" PRId64, value);
-    qstring_append(qstring, num);
+    va_start(ap, fmt);
+    qstring_append_vprintf(qstring, fmt, ap);
+    va_end(ap);
+}
+
+void qstring_append_vprintf(QString *qstring, const char *fmt, va_list ap)
+{
+    va_list ap2;
+    int len;
+
+    va_copy(ap2, ap);
+    len = vsnprintf(NULL, 0, fmt, ap2);
+    assert(len >= 0);
+    va_end(ap2);
+
+    capacity_increase(qstring, len);
+    vsnprintf(qstring->string + qstring->length, len + 1, fmt, ap);
+    qstring->length += len;
 }

 /**
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 19/28] qapi: Use qstring_append_chr() where appropriate
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (17 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 18/28] qapi: Add qstring_append_printf() Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 20/28] qstring: Add qstring_consume_str() Eric Blake
                   ` (12 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino

qstring_append_chr() is more efficient than qstring_append()
when dealing with a one-byte string (including the case of a
temporary 2-byte buffer just for creating a dynamic one-byte
string).

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

---
v4: also convert qstring_append() of one-byte strings
v3: no change
v2: no change
---
 qobject/json-parser.c  | 25 ++++++++++---------------
 qobject/qobject-json.c | 12 ++++++------
 2 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index c18e48a..5c23740 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -143,39 +143,39 @@ static QString *qstring_from_escaped_str(JSONParserContext *ctxt,

             switch (*ptr) {
             case '"':
-                qstring_append(str, "\"");
+                qstring_append_chr(str, '"');
                 ptr++;
                 break;
             case '\'':
-                qstring_append(str, "'");
+                qstring_append_chr(str, '\'');
                 ptr++;
                 break;
             case '\\':
-                qstring_append(str, "\\");
+                qstring_append_chr(str, '\\');
                 ptr++;
                 break;
             case '/':
-                qstring_append(str, "/");
+                qstring_append_chr(str, '/');
                 ptr++;
                 break;
             case 'b':
-                qstring_append(str, "\b");
+                qstring_append_chr(str, '\b');
                 ptr++;
                 break;
             case 'f':
-                qstring_append(str, "\f");
+                qstring_append_chr(str, '\f');
                 ptr++;
                 break;
             case 'n':
-                qstring_append(str, "\n");
+                qstring_append_chr(str, '\n');
                 ptr++;
                 break;
             case 'r':
-                qstring_append(str, "\r");
+                qstring_append_chr(str, '\r');
                 ptr++;
                 break;
             case 't':
-                qstring_append(str, "\t");
+                qstring_append_chr(str, '\t');
                 ptr++;
                 break;
             case 'u': {
@@ -204,12 +204,7 @@ static QString *qstring_from_escaped_str(JSONParserContext *ctxt,
                 goto out;
             }
         } else {
-            char dummy[2];
-
-            dummy[0] = *ptr++;
-            dummy[1] = 0;
-
-            qstring_append(str, dummy);
+            qstring_append_chr(str, *ptr++);
         }
     }

diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 769816d..95de587 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -140,12 +140,12 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         s.str = str;
         s.indent = indent + 1;
         s.pretty = pretty;
-        qstring_append(str, "{");
+        qstring_append_chr(str, '{');
         qdict_iter(val, to_json_dict_iter, &s);
         if (pretty) {
             qstring_append_printf(str, "\n%*s", 4 * indent, "");
         }
-        qstring_append(str, "}");
+        qstring_append_chr(str, '}');
         break;
     }
     case QTYPE_QLIST: {
@@ -156,12 +156,12 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
         s.str = str;
         s.indent = indent + 1;
         s.pretty = pretty;
-        qstring_append(str, "[");
+        qstring_append_chr(str, '[');
         qlist_iter(val, (void *)to_json_list_iter, &s);
         if (pretty) {
             qstring_append_printf(str, "\n%*s", 4 * indent, "");
         }
-        qstring_append(str, "]");
+        qstring_append_chr(str, ']');
         break;
     }
     case QTYPE_QFLOAT: {
@@ -217,7 +217,7 @@ int qstring_append_json_string(QString *qstring, const char *str)
     char *end;
     int result = 0;

-    qstring_append(qstring, "\"");
+    qstring_append_chr(qstring, '"');

     for (ptr = str; *ptr; ptr = end) {
         cp = mod_utf8_codepoint(ptr, 6, &end);
@@ -261,7 +261,7 @@ int qstring_append_json_string(QString *qstring, const char *str)
         }
     };

-    qstring_append(qstring, "\"");
+    qstring_append_chr(qstring, '"');
     return result;
 }

-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 20/28] qstring: Add qstring_consume_str()
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (18 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 19/28] qapi: Use qstring_append_chr() where appropriate Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str() Eric Blake
                   ` (11 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino, Andreas Färber

Similar to g_string_free(), there are cases where we want to
destroy our reference to a QString while grabbing its contents,
where we want to avoid use-after-free but also avoid a needless
strdup(). But unlike g_string_free(), we are at least sensible
enough to add this feature via a different function name,
instead of trying to overload two completely separate concepts
into a single g_string_free().  Do this by introducing
qstring_consume_str(), then use it where it makes sense.

In the case of monitor.c, note that QString _always_ has a
non-NULL embedded string with at least one byte allocated for
a terminating NUL, so special-casing on length 0 was wasted code.

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

---
v4: new patch, suggested by Markus
---
 include/qapi/qmp/qstring.h |  1 +
 monitor.c                  |  6 +-----
 qobject/qstring.c          | 23 +++++++++++++++++++++++
 qom/object.c               |  3 +--
 tests/check-qstring.c      | 31 +++++++++++++++++++++++++++----
 5 files changed, 53 insertions(+), 11 deletions(-)

diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h
index a987f3b..2d55c87 100644
--- a/include/qapi/qmp/qstring.h
+++ b/include/qapi/qmp/qstring.h
@@ -27,6 +27,7 @@ QString *qstring_from_str(const char *str);
 QString *qstring_from_substr(const char *str, int start, int end);
 size_t qstring_get_length(const QString *qstring);
 const char *qstring_get_str(const QString *qstring);
+char *qstring_consume_str(QString *qstring);
 void qstring_append(QString *qstring, const char *str);
 void qstring_append_chr(QString *qstring, int c);
 void qstring_append_printf(QString *qstring, const char *fmt, ...)
diff --git a/monitor.c b/monitor.c
index 2f56488..934d539 100644
--- a/monitor.c
+++ b/monitor.c
@@ -661,11 +661,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
     cur_mon = old_mon;

     qemu_mutex_lock(&hmp.out_lock);
-    if (qstring_get_length(hmp.outbuf) > 0) {
-        output = g_strdup(qstring_get_str(hmp.outbuf));
-    } else {
-        output = g_strdup("");
-    }
+    output = qstring_consume_str(hmp.outbuf);
     qemu_mutex_unlock(&hmp.out_lock);

 out:
diff --git a/qobject/qstring.c b/qobject/qstring.c
index fbfae27..7a438e9 100644
--- a/qobject/qstring.c
+++ b/qobject/qstring.c
@@ -145,6 +145,29 @@ const char *qstring_get_str(const QString *qstring)
 }

 /**
+ * qstring_consume_str(): Destructively convert a QString to string
+ *
+ * The reference count of @qstring is decremented by one, and the
+ * caller is responsible for calling g_free() on the result.  In the
+ * common case where @qstring was not shared, this is faster than
+ * using strdup() on the result of qstring_get_str(); otherwise, the
+ * result is a copy and remaining users of @qstring are unaffected.
+ */
+char *qstring_consume_str(QString *qstring)
+{
+    char *result;
+
+    if (qstring->base.refcnt == 1) {
+        result = qstring->string;
+        qstring->string = NULL;
+    } else {
+        result = g_strdup(qstring->string);
+    }
+    qobject_decref(&qstring->base);
+    return result;
+}
+
+/**
  * qstring_destroy_obj(): Free all memory allocated by a QString
  * object
  */
diff --git a/qom/object.c b/qom/object.c
index 26e7d58..7960729 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1106,10 +1106,9 @@ char *object_property_get_str(Object *obj, const char *name,
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string");
         retval = NULL;
     } else {
-        retval = g_strdup(qstring_get_str(qstring));
+        retval = qstring_consume_str(qstring);
     }

-    QDECREF(qstring);
     return retval;
 }

diff --git a/tests/check-qstring.c b/tests/check-qstring.c
index 9877b42..e6f58e0 100644
--- a/tests/check-qstring.c
+++ b/tests/check-qstring.c
@@ -1,7 +1,7 @@
 /*
  * QString unit-tests.
  *
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2016 Red Hat Inc.
  *
  * Authors:
  *  Luiz Capitulino <lcapitulino@redhat.com>
@@ -32,9 +32,7 @@ static void qstring_from_str_test(void)
     g_assert(strcmp(str, qstring->string) == 0);
     g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);

-    // destroy doesn't exit yet
-    g_free(qstring->string);
-    g_free(qstring);
+    QDECREF(qstring);
 }

 static void qstring_destroy_test(void)
@@ -56,6 +54,30 @@ static void qstring_get_str_test(void)
     QDECREF(qstring);
 }

+static void qstring_consume_str_test(void)
+{
+    QString *qstring;
+    char *ret_str;
+    const char *str = "QEMU/KVM";
+    char *ptr;
+
+    qstring = qstring_from_str(str);
+    QINCREF(qstring);
+    g_assert_cmpint(qstring->base.refcnt, ==, 2);
+    ptr = qstring->string;
+
+    ret_str = qstring_consume_str(qstring);
+    g_assert_cmpint(qstring->base.refcnt, ==, 1);
+    g_assert(ret_str != ptr);
+    g_assert_cmpstr(ret_str, ==, str);
+    g_free(ret_str);
+
+    ret_str = qstring_consume_str(qstring);
+    g_assert(ret_str == ptr);
+    g_assert_cmpstr(ret_str, ==, str);
+    g_free(ret_str);
+}
+
 static void qstring_append_chr_test(void)
 {
     int i;
@@ -103,6 +125,7 @@ int main(int argc, char **argv)
     g_test_add_func("/public/append_chr", qstring_append_chr_test);
     g_test_add_func("/public/from_substr", qstring_from_substr_test);
     g_test_add_func("/public/to_qstring", qobject_to_qstring_test);
+    g_test_add_func("/public/consume_str", qstring_consume_str_test);

     return g_test_run();
 }
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str()
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (19 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 20/28] qstring: Add qstring_consume_str() Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-02 15:21   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls Eric Blake
                   ` (10 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Chrysostomos Nanakos, Jeff Cody,
	Luiz Capitulino, open list:Block layer core

Several spots in the code malloc a string, then wrap it in a
QString, which has to duplicate the input.  Adding a new
constructor with transfer semantics makes this pattern more
efficient, comparable to the just-added transfer semantics to
go from QString back to raw string.  Use the new
qstring_wrap_str() where it makes sense.

The new test still passes under valgrind.

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

---
v4: new patch
---
 include/qapi/qmp/qstring.h |  1 +
 block.c                    |  3 ++-
 block/archipelago.c        |  6 ++----
 blockdev.c                 |  3 +--
 qobject/qstring.c          | 25 +++++++++++++++++++++++++
 tests/check-qstring.c      | 24 ++++++++++++++++++++++++
 6 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h
index 2d55c87..97cf776 100644
--- a/include/qapi/qmp/qstring.h
+++ b/include/qapi/qmp/qstring.h
@@ -25,6 +25,7 @@ typedef struct QString {
 QString *qstring_new(void);
 QString *qstring_from_str(const char *str);
 QString *qstring_from_substr(const char *str, int start, int end);
+QString *qstring_wrap_str(char *str);
 size_t qstring_get_length(const QString *qstring);
 const char *qstring_get_str(const QString *qstring);
 char *qstring_consume_str(QString *qstring);
diff --git a/block.c b/block.c
index 551832f..32d06b5 100644
--- a/block.c
+++ b/block.c
@@ -1448,7 +1448,8 @@ static int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags,
     qdict_put(snapshot_options, "file.driver",
               qstring_from_str("file"));
     qdict_put(snapshot_options, "file.filename",
-              qstring_from_str(tmp_filename));
+              qstring_wrap_str(tmp_filename));
+    tmp_filename = NULL;
     qdict_put(snapshot_options, "driver",
               qstring_from_str("qcow2"));

diff --git a/block/archipelago.c b/block/archipelago.c
index 104f2f9..bfdc14c 100644
--- a/block/archipelago.c
+++ b/block/archipelago.c
@@ -426,13 +426,11 @@ static void archipelago_parse_filename(const char *filename, QDict *options,
     parse_filename_opts(filename, errp, &volume, &segment_name, &mport, &vport);

     if (volume) {
-        qdict_put(options, ARCHIPELAGO_OPT_VOLUME, qstring_from_str(volume));
-        g_free(volume);
+        qdict_put(options, ARCHIPELAGO_OPT_VOLUME, qstring_wrap_str(volume));
     }
     if (segment_name) {
         qdict_put(options, ARCHIPELAGO_OPT_SEGMENT,
-                  qstring_from_str(segment_name));
-        g_free(segment_name);
+                  qstring_wrap_str(segment_name));
     }
     if (mport != NoPort) {
         qdict_put(options, ARCHIPELAGO_OPT_MPORT, qint_from_int(mport));
diff --git a/blockdev.c b/blockdev.c
index adc5058..f293cb3 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1089,8 +1089,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
             new_id = g_strdup_printf("%s%s%i", if_name[type],
                                      mediastr, unit_id);
         }
-        qdict_put(bs_opts, "id", qstring_from_str(new_id));
-        g_free(new_id);
+        qdict_put(bs_opts, "id", qstring_wrap_str(new_id));
     }

     /* Add virtio block device */
diff --git a/qobject/qstring.c b/qobject/qstring.c
index 7a438e9..2af1914 100644
--- a/qobject/qstring.c
+++ b/qobject/qstring.c
@@ -66,6 +66,31 @@ QString *qstring_from_str(const char *str)
     return qstring_from_substr(str, 0, strlen(str) - 1);
 }

+/**
+ * qstring_wrap_str(): Create a new QString around a malloc'd C string
+ *
+ * Returns a strong reference, and caller must not use @str any more.
+ * @str may be NULL, in which case the QString will be "".
+ */
+QString *qstring_wrap_str(char *str)
+{
+    QString *qstring;
+
+    qstring = g_malloc(sizeof(*qstring));
+    qobject_init(QOBJECT(qstring), QTYPE_QSTRING);
+
+    if (str) {
+        qstring->string = str;
+        qstring->length = strlen(str);
+    } else {
+        qstring->string = g_strdup("");
+        qstring->length = 0;
+    }
+    qstring->capacity = qstring->length;
+
+    return qstring;
+}
+
 static void capacity_increase(QString *qstring, size_t len)
 {
     if (qstring->capacity < (qstring->length + len)) {
diff --git a/tests/check-qstring.c b/tests/check-qstring.c
index e6f58e0..0e2e9bd 100644
--- a/tests/check-qstring.c
+++ b/tests/check-qstring.c
@@ -35,6 +35,29 @@ static void qstring_from_str_test(void)
     QDECREF(qstring);
 }

+static void qstring_wrap_str_test(void)
+{
+    QString *qstring;
+    char *str = g_strdup("QEMU");
+
+    qstring = qstring_wrap_str(str);
+    g_assert(qstring != NULL);
+    g_assert(qstring->base.refcnt == 1);
+    g_assert(strcmp("QEMU", qstring->string) == 0);
+    g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
+
+    QDECREF(qstring);
+
+    str = NULL;
+    qstring = qstring_wrap_str(str);
+    g_assert(qstring != NULL);
+    g_assert(qstring->base.refcnt == 1);
+    g_assert(strcmp("", qstring->string) == 0);
+    g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
+
+    QDECREF(qstring);
+}
+
 static void qstring_destroy_test(void)
 {
     QString *qstring = qstring_from_str("destroy test");
@@ -120,6 +143,7 @@ int main(int argc, char **argv)
     g_test_init(&argc, &argv, NULL);

     g_test_add_func("/public/from_str", qstring_from_str_test);
+    g_test_add_func("/public/wrap_str", qstring_wrap_str_test);
     g_test_add_func("/public/destroy", qstring_destroy_test);
     g_test_add_func("/public/get_str", qstring_get_str_test);
     g_test_add_func("/public/append_chr", qstring_append_chr_test);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (20 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str() Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-02 15:32   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 23/28] tests: Test qobject_to_json() pretty formatting Eric Blake
                   ` (9 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Kevin Wolf, Max Reitz, Luiz Capitulino, Michael Roth,
	open list:Block layer core

It's simpler to have a single conversion function that takes a
bool parameter, rather than two functions where the choice of
function determines an internal bool.  Similar to commit fc471c18.

While at it, the conversion currently cannot fail (maybe it SHOULD
be possible to choose to fail, when encountering invalid UTF-8
encoding or an Infinity or NaN valued double, but that's a story
for another day), so clean up callers to avoid a needless assert.
And use bool rather than int for 'pretty'.

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

---
v4: new patch
---
 include/qapi/qmp/qobject-json.h    |  4 ++--
 block.c                            |  2 +-
 monitor.c                          |  4 +---
 qemu-img.c                         |  9 +++------
 qga/main.c                         |  5 +----
 qobject/qobject-json.c             | 19 +++++--------------
 tests/check-qobject-json.c         | 27 +++++++++++----------------
 tests/libqtest.c                   |  2 +-
 tests/test-visitor-serialization.c |  2 +-
 9 files changed, 26 insertions(+), 48 deletions(-)

diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index 0749e7e..e4d11cf 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -21,8 +21,8 @@ QObject *qobject_from_json(const char *string);
 QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
 QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0);

-QString *qobject_to_json(const QObject *obj);
-QString *qobject_to_json_pretty(const QObject *obj);
+/* Convert the object to QString; does not fail. */
+QString *qobject_to_json(const QObject *obj, bool pretty);

 int qstring_append_json_string(QString *qstring, const char *str);
 int qstring_append_json_number(QString *qstring, double number);
diff --git a/block.c b/block.c
index 32d06b5..7f80d31 100644
--- a/block.c
+++ b/block.c
@@ -4006,7 +4006,7 @@ void bdrv_refresh_filename(BlockDriverState *bs)
     if (bs->exact_filename[0]) {
         pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
     } else if (bs->full_open_options) {
-        QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
+        QString *json = qobject_to_json(QOBJECT(bs->full_open_options), false);
         snprintf(bs->filename, sizeof(bs->filename), "json:%s",
                  qstring_get_str(json));
         QDECREF(json);
diff --git a/monitor.c b/monitor.c
index 934d539..f381673 100644
--- a/monitor.c
+++ b/monitor.c
@@ -389,9 +389,7 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data)
 {
     QString *json;

-    json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
-                                             qobject_to_json(data);
-    assert(json != NULL);
+    json = qobject_to_json(data, mon->flags & MONITOR_USE_PRETTY);

     qstring_append_chr(json, '\n');
     monitor_puts(mon, qstring_get_str(json));
diff --git a/qemu-img.c b/qemu-img.c
index 39bff77..086530f 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -485,8 +485,7 @@ static void dump_json_image_check(ImageCheck *check, bool quiet)

     visit_type_ImageCheck(v, NULL, &check, &error_abort);
     visit_complete(v, &obj);
-    str = qobject_to_json_pretty(obj);
-    assert(str != NULL);
+    str = qobject_to_json(obj, true);
     qprintf(quiet, "%s\n", qstring_get_str(str));
     qobject_decref(obj);
     visit_free(v);
@@ -2175,8 +2174,7 @@ static void dump_json_image_info_list(ImageInfoList *list)

     visit_type_ImageInfoList(v, NULL, &list, &error_abort);
     visit_complete(v, &obj);
-    str = qobject_to_json_pretty(obj);
-    assert(str != NULL);
+    str = qobject_to_json(obj, true);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
     visit_free(v);
@@ -2191,8 +2189,7 @@ static void dump_json_image_info(ImageInfo *info)

     visit_type_ImageInfo(v, NULL, &info, &error_abort);
     visit_complete(v, &obj);
-    str = qobject_to_json_pretty(obj);
-    assert(str != NULL);
+    str = qobject_to_json(obj, true);
     printf("%s\n", qstring_get_str(str));
     qobject_decref(obj);
     visit_free(v);
diff --git a/qga/main.c b/qga/main.c
index 1c0315e..13545b2 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -523,10 +523,7 @@ static int send_response(GAState *s, QObject *payload)

     g_assert(payload && s->channel);

-    payload_qstr = qobject_to_json(payload);
-    if (!payload_qstr) {
-        return -EINVAL;
-    }
+    payload_qstr = qobject_to_json(payload, false);

     if (s->delimit_response) {
         s->delimit_response = false;
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 95de587..9ace92b 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -72,12 +72,12 @@ QObject *qobject_from_jsonf(const char *string, ...)
 typedef struct ToJsonIterState
 {
     int indent;
-    int pretty;
+    bool pretty;
     int count;
     QString *str;
 } ToJsonIterState;

-static void to_json(const QObject *obj, QString *str, int pretty, int indent);
+static void to_json(const QObject *obj, QString *str, bool pretty, int indent);

 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
@@ -114,7 +114,7 @@ static void to_json_list_iter(QObject *obj, void *opaque)
     s->count++;
 }

-static void to_json(const QObject *obj, QString *str, int pretty, int indent)
+static void to_json(const QObject *obj, QString *str, bool pretty, int indent)
 {
     switch (qobject_type(obj)) {
     case QTYPE_QNULL:
@@ -185,20 +185,11 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
     }
 }

-QString *qobject_to_json(const QObject *obj)
+QString *qobject_to_json(const QObject *obj, bool pretty)
 {
     QString *str = qstring_new();

-    to_json(obj, str, 0, 0);
-
-    return str;
-}
-
-QString *qobject_to_json_pretty(const QObject *obj)
-{
-    QString *str = qstring_new();
-
-    to_json(obj, str, 1, 0);
+    to_json(obj, str, pretty, 0);

     return str;
 }
diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
index d889501..9814282 100644
--- a/tests/check-qobject-json.c
+++ b/tests/check-qobject-json.c
@@ -64,7 +64,7 @@ static void escaped_string(void)
         g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);

         if (test_cases[i].skip == 0) {
-            str = qobject_to_json(obj);
+            str = qobject_to_json(obj, false);
             g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
             qobject_decref(obj);
         }
@@ -98,7 +98,7 @@ static void simple_string(void)
         str = qobject_to_qstring(obj);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);

-        str = qobject_to_json(obj);
+        str = qobject_to_json(obj, false);
         g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);

         qobject_decref(obj);
@@ -832,13 +832,8 @@ static void utf8_string(void)
         qobject_decref(obj);

         obj = QOBJECT(qstring_from_str(utf8_in));
-        str = qobject_to_json(obj);
-        if (json_out) {
-            g_assert(str);
-            g_assert_cmpstr(qstring_get_str(str), ==, json_out);
-        } else {
-            g_assert(!str);
-        }
+        str = qobject_to_json(obj, false);
+        g_assert_cmpstr(qstring_get_str(str), ==, json_out);
         QDECREF(str);
         qobject_decref(obj);

@@ -913,7 +908,7 @@ static void simple_number(void)
         if (test_cases[i].skip == 0) {
             QString *str;

-            str = qobject_to_json(obj);
+            str = qobject_to_json(obj, false);
             g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
             QDECREF(str);
         }
@@ -951,7 +946,7 @@ static void float_number(void)
         if (test_cases[i].skip == 0) {
             QString *str;

-            str = qobject_to_json(obj);
+            str = qobject_to_json(obj, false);
             g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
             QDECREF(str);
         }
@@ -1011,7 +1006,7 @@ static void keyword_literal(void)
     qbool = qobject_to_qbool(obj);
     g_assert(qbool_get_bool(qbool) == true);

-    str = qobject_to_json(obj);
+    str = qobject_to_json(obj, false);
     g_assert(strcmp(qstring_get_str(str), "true") == 0);
     QDECREF(str);

@@ -1024,7 +1019,7 @@ static void keyword_literal(void)
     qbool = qobject_to_qbool(obj);
     g_assert(qbool_get_bool(qbool) == false);

-    str = qobject_to_json(obj);
+    str = qobject_to_json(obj, false);
     g_assert(strcmp(qstring_get_str(str), "false") == 0);
     QDECREF(str);

@@ -1191,7 +1186,7 @@ static void simple_dict(void)

         g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);

-        str = qobject_to_json(obj);
+        str = qobject_to_json(obj, false);
         qobject_decref(obj);

         obj = qobject_from_json(qstring_get_str(str));
@@ -1306,7 +1301,7 @@ static void simple_list(void)

         g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);

-        str = qobject_to_json(obj);
+        str = qobject_to_json(obj, false);
         qobject_decref(obj);

         obj = qobject_from_json(qstring_get_str(str));
@@ -1374,7 +1369,7 @@ static void simple_whitespace(void)

         g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);

-        str = qobject_to_json(obj);
+        str = qobject_to_json(obj, false);
         qobject_decref(obj);

         obj = qobject_from_json(qstring_get_str(str));
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 2f42bc9..5371fc8 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -421,7 +421,7 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap)
     /* No need to send anything for an empty QObject.  */
     if (qobj) {
         int log = getenv("QTEST_LOG") != NULL;
-        QString *qstr = qobject_to_json(qobj);
+        QString *qstr = qobject_to_json(qobj, false);
         const char *str = qstring_get_str(qstr);
         size_t size = qstring_get_length(qstr);

diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 4da4b8e..7a1c071 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1037,7 +1037,7 @@ static void qmp_deserialize(void **native_out, void *datap,

     visit_complete(d->qov, &d->obj);
     obj_orig = d->obj;
-    output_json = qobject_to_json(obj_orig);
+    output_json = qobject_to_json(obj_orig, false);
     obj = qobject_from_json(qstring_get_str(output_json));

     QDECREF(output_json);
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 23/28] tests: Test qobject_to_json() pretty formatting
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (21 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor Eric Blake
                   ` (8 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino

It's risky to refactor qobject_to_json() without at least testing
that pretty output remains unchanged :)

Note that the new simple_pretty() test is a bit sensitive to our
current notion of prettiness, as well as to the hash ordering in
QDict (most of the tests in check-qobject-json intentionally do
not compare the original string to the round-trip string, because
we liberally accept more input forms than the canonical form that
we output).

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

---
v4: new patch, split from v3 12/18
---
 tests/check-qobject-json.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
index 9814282..267fc67 100644
--- a/tests/check-qobject-json.c
+++ b/tests/check-qobject-json.c
@@ -1,6 +1,6 @@
 /*
  * Copyright IBM, Corp. 2009
- * Copyright (c) 2013, 2015 Red Hat Inc.
+ * Copyright (c) 2013-2016 Red Hat Inc.
  *
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
@@ -1383,6 +1383,69 @@ static void simple_whitespace(void)
     }
 }

+static void simple_pretty(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        LiteralQObject decoded;
+    } test_cases[] = {
+        {
+            .encoded =
+            "[\n"
+            "    43,\n"
+            "    42\n"
+            "]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        {
+            .encoded =
+            "[\n"
+            "    43,\n"
+            "    {\n"
+            "        \"a\": 32,\n"
+            "        \"h\": \"b\"\n"
+            "    },\n"
+            "    [\n"
+            "    ],\n"
+            "    42\n"
+            "]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QDICT(((LiteralQDictEntry[]){
+                                    { "a", QLIT_QINT(32) },
+                                    { "h", QLIT_QSTR("b") },
+                                    { } })),
+                        QLIT_QLIST(((LiteralQObject[]){
+                                    { } })),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        { }
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+        str = qobject_to_json(obj, true);
+        qobject_decref(obj);
+        g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
+        QDECREF(str);
+    }
+}
+
 static void simple_varargs(void)
 {
     QObject *embedded_obj;
@@ -1520,6 +1583,7 @@ int main(int argc, char **argv)
     g_test_add_func("/lists/simple_list", simple_list);

     g_test_add_func("/whitespace/simple_whitespace", simple_whitespace);
+    g_test_add_func("/whitespace/simple_pretty", simple_pretty);

     g_test_add_func("/varargs/simple_varargs", simple_varargs);

-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (22 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 23/28] tests: Test qobject_to_json() pretty formatting Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-03  7:39   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in " Eric Blake
                   ` (7 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

We have several places that want to go from qapi to JSON; right now,
they have to create an intermediate QObject to do the work.  That
also has the drawback that the JSON formatting of a QDict will
rearrange keys (according to a deterministic, but unpredictable,
hash), when humans have an easier time if dicts are produced in
the same order as the qapi type.

For these reasons, it is time to add a new JSON output visitor.
This patch just adds the basic visitor and tests that it works;
later patches will add pretty-printing, support for visit_type_any(),
and conversion of clients to use the visitor.

Design choices: Unlike the QMP output visitor, the JSON visitor
refuses to visit a required string with a NULL value, via abort().
Reusing QString to grow the contents means that we easily share
code with both qobject-json.c and qjson.c; although it might be
nice to enhance things to take an optional output callback
function so that the output can truly be streamed instead of
collected in memory.

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

---
v4: retitle, split off inf/NaN handling, rebase to visit_complete
and visit_free changes, defer type_any, address other findings from
Markus
v3: retitle, rebase to master, minor cleanups
v2: rebase to qapi subset E v8; add test of error outputting
infinity; use unsigned depth
---
 include/qapi/visitor.h             |  33 ++--
 include/qapi/json-output-visitor.h |  28 +++
 qapi/json-output-visitor.c         | 193 +++++++++++++++++++
 tests/test-json-output-visitor.c   | 383 +++++++++++++++++++++++++++++++++++++
 tests/test-qmp-output-visitor.c    |   5 +
 qapi/Makefile.objs                 |   2 +-
 tests/.gitignore                   |   1 +
 tests/Makefile                     |   5 +-
 8 files changed, 632 insertions(+), 18 deletions(-)
 create mode 100644 include/qapi/json-output-visitor.h
 create mode 100644 qapi/json-output-visitor.c
 create mode 100644 tests/test-json-output-visitor.c

diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 3f46921..c097507 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -26,17 +26,17 @@
  *
  * There are four 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, the
- * dealloc visitor can take a QAPI graph (possibly partially
- * constructed) and recursively free its resources, and the clone
- * visitor performs a deep clone of one QAPI object to another.  While
- * the dealloc and QMP input/output visitors are general, the string,
- * QemuOpts, and clone 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.
+ * the corresponding QAPI graph, output visitors (QMP, string, and
+ * JSON) take a completed QAPI graph and generate an external
+ * representation, the dealloc visitor can take a QAPI graph (possibly
+ * partially constructed) and recursively free its resources, and the
+ * clone visitor performs a deep clone of one QAPI object to another.
+ * While the dealloc, JSON output, and QMP input/output visitors are
+ * general, the string, QemuOpts, and clone 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 of the visitors are created via:
  *
@@ -59,11 +59,12 @@
  * 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.
+ * The @name parameter of visit_type_FOO() and visit_start_OBJECT()
+ * 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
diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
new file mode 100644
index 0000000..41c79f4
--- /dev/null
+++ b/include/qapi/json-output-visitor.h
@@ -0,0 +1,28 @@
+/*
+ * JSON Output Visitor
+ *
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef JSON_OUTPUT_VISITOR_H
+#define JSON_OUTPUT_VISITOR_H
+
+#include "qapi/visitor.h"
+
+typedef struct JsonOutputVisitor JsonOutputVisitor;
+
+/*
+ * Create a new JSON output visitor.
+ *
+ * If everything else succeeds, pass @result to visit_complete() to
+ * collect the result of the visit.
+ *
+ * For now, this cannot be used to visit the 'any' type.
+ */
+Visitor *json_output_visitor_new(char **result);
+
+#endif
diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
new file mode 100644
index 0000000..7010ff8
--- /dev/null
+++ b/qapi/json-output-visitor.c
@@ -0,0 +1,193 @@
+/*
+ * Convert QAPI to JSON
+ *
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/json-output-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qobject-json.h"
+
+struct JsonOutputVisitor {
+    Visitor visitor;
+    QString *str;
+    bool comma;
+    unsigned int depth;
+    char **result;
+};
+
+static JsonOutputVisitor *to_jov(Visitor *v)
+{
+    return container_of(v, JsonOutputVisitor, visitor);
+}
+
+static void json_output_name(JsonOutputVisitor *jov, const char *name)
+{
+    if (jov->comma) {
+        qstring_append(jov->str, ", ");
+    } else {
+        jov->comma = true;
+    }
+    if (name && jov->depth) {
+        qstring_append_json_string(jov->str, name);
+        qstring_append(jov->str, ": ");
+    }
+}
+
+static void json_output_start_struct(Visitor *v, const char *name, void **obj,
+                                     size_t unused, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    qstring_append(jov->str, "{");
+    jov->comma = false;
+    jov->depth++;
+}
+
+static void json_output_end_struct(Visitor *v, void **obj)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    assert(jov->depth);
+    jov->depth--;
+    qstring_append(jov->str, "}");
+    jov->comma = true;
+}
+
+static void json_output_start_list(Visitor *v, const char *name,
+                                   GenericList **listp, size_t size,
+                                   Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    qstring_append(jov->str, "[");
+    jov->comma = false;
+    jov->depth++;
+}
+
+static GenericList *json_output_next_list(Visitor *v, GenericList *tail,
+                                          size_t size)
+{
+    return tail->next;
+}
+
+static void json_output_end_list(Visitor *v, void **obj)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    assert(jov->depth);
+    jov->depth--;
+    qstring_append(jov->str, "]");
+    jov->comma = true;
+}
+
+static void json_output_type_int64(Visitor *v, const char *name, int64_t *obj,
+                                   Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    qstring_append_printf(jov->str, "%" PRId64, *obj);
+}
+
+static void json_output_type_uint64(Visitor *v, const char *name,
+                                    uint64_t *obj, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    qstring_append_printf(jov->str, "%" PRIu64, *obj);
+}
+
+static void json_output_type_bool(Visitor *v, const char *name, bool *obj,
+                                  Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    qstring_append(jov->str, *obj ? "true" : "false");
+}
+
+static void json_output_type_str(Visitor *v, const char *name, char **obj,
+                                 Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    assert(*obj);
+    json_output_name(jov, name);
+    /* FIXME: report invalid UTF-8 encoding */
+    qstring_append_json_string(jov->str, *obj);
+}
+
+static void json_output_type_number(Visitor *v, const char *name, double *obj,
+                                    Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    /* FIXME: report Inf/NaN problems */
+    qstring_append_json_number(jov->str, *obj);
+}
+
+static void json_output_type_null(Visitor *v, const char *name, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    json_output_name(jov, name);
+    qstring_append(jov->str, "null");
+}
+
+static void json_output_complete(Visitor *v, void *result)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    assert(!jov->depth);
+    assert(qstring_get_length(jov->str));
+    assert(jov->result == result);
+    *jov->result = qstring_consume_str(jov->str);
+    jov->str = NULL;
+    jov->result = NULL;
+}
+
+static void json_output_free(Visitor *v)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+
+    QDECREF(jov->str);
+    g_free(jov);
+}
+
+Visitor *json_output_visitor_new(char **result)
+{
+    JsonOutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+    v->result = result;
+    *result = NULL;
+    v->str = qstring_new();
+
+    v->visitor.type = VISITOR_OUTPUT;
+    v->visitor.start_struct = json_output_start_struct;
+    v->visitor.end_struct = json_output_end_struct;
+    v->visitor.start_list = json_output_start_list;
+    v->visitor.next_list = json_output_next_list;
+    v->visitor.end_list = json_output_end_list;
+    v->visitor.type_int64 = json_output_type_int64;
+    v->visitor.type_uint64 = json_output_type_uint64;
+    v->visitor.type_bool = json_output_type_bool;
+    v->visitor.type_str = json_output_type_str;
+    v->visitor.type_number = json_output_type_number;
+    v->visitor.type_null = json_output_type_null;
+    v->visitor.complete = json_output_complete;
+    v->visitor.free = json_output_free;
+
+    return &v->visitor;
+}
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
new file mode 100644
index 0000000..3c77a61
--- /dev/null
+++ b/tests/test-json-output-visitor.c
@@ -0,0 +1,383 @@
+/*
+ * JSON Output Visitor unit-tests.
+ *
+ * Copyright (C) 2015-2016 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Similar in design to test-qmp-output-visitor; if you add tests
+ * here, consider adding tests there as well.
+ */
+
+#include "qemu/osdep.h"
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/json-output-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qapi/qmp/types.h"
+#include "qapi/error.h"
+
+typedef struct TestOutputVisitorData {
+    Visitor *ov;
+    char *str;
+} TestOutputVisitorData;
+
+static void visitor_output_setup(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    data->ov = json_output_visitor_new(&data->str);
+    g_assert(data->ov);
+}
+
+static void visitor_output_teardown(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    visit_free(data->ov);
+    data->ov = NULL;
+    g_free(data->str);
+    data->str = NULL;
+}
+
+static const char *visitor_get(TestOutputVisitorData *data)
+{
+    visit_complete(data->ov, &data->str);
+    g_assert(data->str);
+    return data->str;
+}
+
+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)
+{
+    int64_t value = -42;
+    const char *out;
+
+    visit_type_int(data->ov, NULL, &value, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "-42");
+}
+
+static void test_visitor_out_bool(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    bool value = true;
+    const char *out;
+
+    visit_type_bool(data->ov, NULL, &value, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "true");
+}
+
+static void test_visitor_out_number(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    double value = 3.14;
+    const char *out;
+
+    visit_type_number(data->ov, NULL, &value, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "3.14");
+
+    /* FIXME: JSON requires finite numbers */
+}
+
+static void test_visitor_out_string(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    char *string = (char *) "Q E M U";
+    const char *out;
+
+    visit_type_str(data->ov, NULL, &string, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "\"Q E M U\"");
+}
+
+static void test_visitor_out_enum(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    const char *out;
+    EnumOne i;
+    size_t len;
+
+    for (i = 0; i < ENUM_ONE__MAX; i++) {
+        visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
+
+        out = visitor_get(data);
+        g_assert(*out == '"');
+        len = strlen(out);
+        g_assert_cmpint(len, >, 2);
+        g_assert(out[len - 1] == '"');
+        g_assert_cmpint(memcmp(out + 1, EnumOne_lookup[i], len - 2), ==, 0);
+        visitor_reset(data);
+    }
+}
+
+static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
+                                         const void *unused)
+{
+    EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 };
+    Error *err;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        err = NULL;
+        visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
+        error_free_or_abort(&err);
+    }
+}
+
+
+static void test_visitor_out_struct(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    TestStruct test_struct = { .integer = 42,
+                               .boolean = false,
+                               .string = (char *) "foo"};
+    TestStruct *p = &test_struct;
+    const char *out;
+
+    visit_type_TestStruct(data->ov, NULL, &p, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==,
+                    "{"
+                     "\"integer\": 42, "
+                     "\"boolean\": false, "
+                     "\"string\": \"foo\""
+                    "}");
+}
+
+static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
+                                           const void *unused)
+{
+    int64_t value = 42;
+    UserDefTwo *ud2;
+    const char *string = "user def string";
+    const char *strings[] = { "forty two", "forty three", "forty four",
+                              "forty five" };
+    const char *out;
+
+    ud2 = g_malloc0(sizeof(*ud2));
+    ud2->string0 = g_strdup(strings[0]);
+
+    ud2->dict1 = g_malloc0(sizeof(*ud2->dict1));
+    ud2->dict1->string1 = g_strdup(strings[1]);
+
+    ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
+    ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
+    ud2->dict1->dict2->userdef->string = g_strdup(string);
+    ud2->dict1->dict2->userdef->integer = value;
+    ud2->dict1->dict2->string = g_strdup(strings[2]);
+
+    ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
+    ud2->dict1->has_dict3 = true;
+    ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
+    ud2->dict1->dict3->userdef->string = g_strdup(string);
+    ud2->dict1->dict3->userdef->integer = value;
+    ud2->dict1->dict3->string = g_strdup(strings[3]);
+
+    visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==,
+                    "{"
+                     "\"string0\": \"forty two\", "
+                     "\"dict1\": {"
+                      "\"string1\": \"forty three\", "
+                      "\"dict2\": {"
+                       "\"userdef\": {"
+                        "\"integer\": 42, "
+                        "\"string\": \"user def string\""
+                        "}, "
+                       "\"string\": \"forty four\""
+                       "}, "
+                      "\"dict3\": {"
+                       "\"userdef\": {"
+                        "\"integer\": 42, "
+                        "\"string\": \"user def string\""
+                        "}, "
+                      "\"string\": \"forty five\""
+                      "}"
+                     "}"
+                    "}");
+    qapi_free_UserDefTwo(ud2);
+}
+
+static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
+                                           const void *unused)
+{
+    EnumOne bad_values[] = { ENUM_ONE__MAX, -1 };
+    UserDefOne u = { .string = (char *)"" };
+    UserDefOne *pu = &u;
+    Error *err;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values); i++) {
+        err = NULL;
+        u.has_enum1 = true;
+        u.enum1 = bad_values[i];
+        visit_type_UserDefOne(data->ov, "unused", &pu, &err);
+        error_free_or_abort(&err);
+        visitor_reset(data);
+    }
+}
+
+
+static void test_visitor_out_list(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    const char *value_str = "list value";
+    TestStructList *p, *head = NULL;
+    const int max_items = 10;
+    bool value_bool = true;
+    int value_int = 10;
+    int i;
+    const char *out;
+
+    for (i = 0; i < max_items; i++) {
+        p = g_malloc0(sizeof(*p));
+        p->value = g_malloc0(sizeof(*p->value));
+        p->value->integer = value_int + (max_items - i - 1);
+        p->value->boolean = value_bool;
+        p->value->string = g_strdup(value_str);
+
+        p->next = head;
+        head = p;
+    }
+
+    visit_type_TestStructList(data->ov, NULL, &head, &error_abort);
+
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==,
+                    "["
+                     "{\"integer\": 10, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 11, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 12, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 13, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 14, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 15, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 16, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 17, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 18, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 19, \"boolean\": true, "
+                      "\"string\": \"list value\"}"
+                    "]");
+    qapi_free_TestStructList(head);
+}
+
+static void test_visitor_out_union_flat(TestOutputVisitorData *data,
+                                        const void *unused)
+{
+    const char *out;
+    UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion));
+
+    tmp->enum1 = ENUM_ONE_VALUE1;
+    tmp->string = g_strdup("str");
+    tmp->integer = 41;
+    tmp->u.value1.boolean = true;
+
+    visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==,
+                    "{"
+                     "\"integer\": 41, "
+                     "\"string\": \"str\", "
+                     "\"enum1\": \"value1\", "
+                     "\"boolean\": true"
+                    "}");
+    qapi_free_UserDefFlatUnion(tmp);
+}
+
+static void test_visitor_out_alternate(TestOutputVisitorData *data,
+                                       const void *unused)
+{
+    UserDefAlternate *tmp;
+    const char *out;
+
+    tmp = g_new0(UserDefAlternate, 1);
+    tmp->type = QTYPE_QINT;
+    tmp->u.i = 42;
+
+    visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "42");
+    qapi_free_UserDefAlternate(tmp);
+
+    visitor_reset(data);
+    tmp = g_new0(UserDefAlternate, 1);
+    tmp->type = QTYPE_QSTRING;
+    tmp->u.s = g_strdup("hello");
+
+    visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "\"hello\"");
+    qapi_free_UserDefAlternate(tmp);
+}
+
+static void test_visitor_out_null(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    const char *out;
+
+    visit_type_null(data->ov, NULL, &error_abort);
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "null");
+}
+
+static void output_visitor_test_add(const char *testpath,
+                                    void (*test_func)(TestOutputVisitorData *,
+                                                      const void *))
+{
+    g_test_add(testpath, TestOutputVisitorData, NULL, visitor_output_setup,
+               test_func, visitor_output_teardown);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    output_visitor_test_add("/visitor/json/int", test_visitor_out_int);
+    output_visitor_test_add("/visitor/json/bool", test_visitor_out_bool);
+    output_visitor_test_add("/visitor/json/number", test_visitor_out_number);
+    output_visitor_test_add("/visitor/json/string", test_visitor_out_string);
+    output_visitor_test_add("/visitor/json/enum", test_visitor_out_enum);
+    output_visitor_test_add("/visitor/json/enum-errors",
+                            test_visitor_out_enum_errors);
+    output_visitor_test_add("/visitor/json/struct", test_visitor_out_struct);
+    output_visitor_test_add("/visitor/json/struct-nested",
+                            test_visitor_out_struct_nested);
+    output_visitor_test_add("/visitor/json/struct-errors",
+                            test_visitor_out_struct_errors);
+    output_visitor_test_add("/visitor/json/list", test_visitor_out_list);
+    output_visitor_test_add("/visitor/json/union-flat",
+                            test_visitor_out_union_flat);
+    output_visitor_test_add("/visitor/json/alternate",
+                            test_visitor_out_alternate);
+    output_visitor_test_add("/visitor/json/null", test_visitor_out_null);
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index df8a495..0db0b19 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -10,6 +10,11 @@
  * See the COPYING file in the top-level directory.
  */

+/*
+ * Similar in design to test-json-output-visitor; if you add tests
+ * here, consider adding tests there as well.
+ */
+
 #include "qemu/osdep.h"
 #include <glib.h>

diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 7ea4aeb..0a08492 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,6 +1,6 @@
 util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
-util-obj-y += opts-visitor.o qapi-clone-visitor.o
+util-obj-y += opts-visitor.o qapi-clone-visitor.o json-output-visitor.o
 util-obj-y += qmp-event.o
 util-obj-y += qapi-util.o
diff --git a/tests/.gitignore b/tests/.gitignore
index 346d75d..60ff7cc 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -41,6 +41,7 @@ test-io-channel-file.txt
 test-io-channel-socket
 test-io-channel-tls
 test-io-task
+test-json-output-visitor
 test-logging
 test-mul64
 test-opts-visitor
diff --git a/tests/Makefile b/tests/Makefile
index d596493..0d06406 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -24,6 +24,8 @@ check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
 gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
 check-unit-y += tests/test-clone-visitor$(EXESUF)
 gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c
+check-unit-y += tests/test-json-output-visitor$(EXESUF)
+gcov-files-test-json-output-visitor-y = qapi/json-output-visitor.c
 check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
 gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c
 check-unit-y += tests/test-qmp-input-strict$(EXESUF)
@@ -390,7 +392,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
 	tests/check-qobject-json.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-clone-visitor.o \
+	tests/test-clone-visitor.o tests/test-json-output-visitor.o \
 	tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
 	tests/test-qmp-commands.o tests/test-visitor-serialization.o \
 	tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
@@ -482,6 +484,7 @@ tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(te
 tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
 tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y)
 tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y)
+tests/test-json-output-visitor$(EXESUF): tests/test-json-output-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y)
 tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y)
 tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in JSON output visitor
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (23 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-03  7:56   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor Eric Blake
                   ` (6 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Similar to pretty printing in the QObject visitor.  The trickiest
part is probably that the testsuite now has to honor parameterization
on whether pretty printing is enabled.

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

---
v4: rebase to earlier changes, defer type_any() to later
v3: rebase to earlier changes
v2: rebase to earlier changes
---
 include/qapi/json-output-visitor.h |   5 +-
 qapi/json-output-visitor.c         |  15 ++++-
 tests/test-json-output-visitor.c   | 122 +++++++++++++++++++++++++------------
 3 files changed, 99 insertions(+), 43 deletions(-)

diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
index 41c79f4..94c9e0f 100644
--- a/include/qapi/json-output-visitor.h
+++ b/include/qapi/json-output-visitor.h
@@ -21,8 +21,11 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
  * If everything else succeeds, pass @result to visit_complete() to
  * collect the result of the visit.
  *
+ * If @pretty, make the output legible with newlines and indentation;
+ * otherwise the output uses a single line.
+ *
  * For now, this cannot be used to visit the 'any' type.
  */
-Visitor *json_output_visitor_new(char **result);
+Visitor *json_output_visitor_new(bool pretty, char **result);

 #endif
diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
index 7010ff8..881c9ee 100644
--- a/qapi/json-output-visitor.c
+++ b/qapi/json-output-visitor.c
@@ -17,6 +17,7 @@
 struct JsonOutputVisitor {
     Visitor visitor;
     QString *str;
+    bool pretty;
     bool comma;
     unsigned int depth;
     char **result;
@@ -30,10 +31,13 @@ static JsonOutputVisitor *to_jov(Visitor *v)
 static void json_output_name(JsonOutputVisitor *jov, const char *name)
 {
     if (jov->comma) {
-        qstring_append(jov->str, ", ");
+        qstring_append(jov->str, jov->pretty ? "," : ", ");
     } else {
         jov->comma = true;
     }
+    if (jov->pretty && jov->depth) {
+        qstring_append_printf(jov->str, "\n%*s", 4 * jov->depth, "");
+    }
     if (name && jov->depth) {
         qstring_append_json_string(jov->str, name);
         qstring_append(jov->str, ": ");
@@ -57,6 +61,9 @@ static void json_output_end_struct(Visitor *v, void **obj)

     assert(jov->depth);
     jov->depth--;
+    if (jov->pretty) {
+        qstring_append_printf(jov->str, "\n%*s", 4 * jov->depth, "");
+    }
     qstring_append(jov->str, "}");
     jov->comma = true;
 }
@@ -85,6 +92,9 @@ static void json_output_end_list(Visitor *v, void **obj)

     assert(jov->depth);
     jov->depth--;
+    if (jov->pretty) {
+        qstring_append_printf(jov->str, "\n%*s", 4 * jov->depth, "");
+    }
     qstring_append(jov->str, "]");
     jov->comma = true;
 }
@@ -165,11 +175,12 @@ static void json_output_free(Visitor *v)
     g_free(jov);
 }

-Visitor *json_output_visitor_new(char **result)
+Visitor *json_output_visitor_new(bool pretty, char **result)
 {
     JsonOutputVisitor *v;

     v = g_malloc0(sizeof(*v));
+    v->pretty = pretty;
     v->result = result;
     *result = NULL;
     v->str = qstring_new();
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
index 3c77a61..5073715 100644
--- a/tests/test-json-output-visitor.c
+++ b/tests/test-json-output-visitor.c
@@ -24,14 +24,17 @@

 typedef struct TestOutputVisitorData {
     Visitor *ov;
+    bool pretty;
     char *str;
 } TestOutputVisitorData;

 static void visitor_output_setup(TestOutputVisitorData *data,
-                                 const void *unused)
+                                 const void *arg)
 {
-    data->ov = json_output_visitor_new(&data->str);
+    const bool *pretty = arg;
+    data->ov = json_output_visitor_new(*pretty, &data->str);
     g_assert(data->ov);
+    data->pretty = *pretty;
 }

 static void visitor_output_teardown(TestOutputVisitorData *data,
@@ -52,8 +55,9 @@ static const char *visitor_get(TestOutputVisitorData *data)

 static void visitor_reset(TestOutputVisitorData *data)
 {
+    bool pretty = data->pretty;
     visitor_output_teardown(data, NULL);
-    visitor_output_setup(data, NULL);
+    visitor_output_setup(data, &pretty);
 }

 static void test_visitor_out_int(TestOutputVisitorData *data,
@@ -161,8 +165,9 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
 }

 static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
-                                           const void *unused)
+                                           const void *arg)
 {
+    const bool *pretty = arg;
     int64_t value = 42;
     UserDefTwo *ud2;
     const char *string = "user def string";
@@ -192,27 +197,51 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
     visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);

     out = visitor_get(data);
-    g_assert_cmpstr(out, ==,
-                    "{"
-                     "\"string0\": \"forty two\", "
-                     "\"dict1\": {"
-                      "\"string1\": \"forty three\", "
-                      "\"dict2\": {"
-                       "\"userdef\": {"
-                        "\"integer\": 42, "
-                        "\"string\": \"user def string\""
-                        "}, "
-                       "\"string\": \"forty four\""
-                       "}, "
-                      "\"dict3\": {"
-                       "\"userdef\": {"
-                        "\"integer\": 42, "
-                        "\"string\": \"user def string\""
-                        "}, "
-                      "\"string\": \"forty five\""
-                      "}"
-                     "}"
-                    "}");
+    if (*pretty) {
+        g_assert_cmpstr(out, ==,
+                        "{\n"
+                        "    \"string0\": \"forty two\",\n"
+                        "    \"dict1\": {\n"
+                        "        \"string1\": \"forty three\",\n"
+                        "        \"dict2\": {\n"
+                        "            \"userdef\": {\n"
+                        "                \"integer\": 42,\n"
+                        "                \"string\": \"user def string\"\n"
+                        "            },\n"
+                        "            \"string\": \"forty four\"\n"
+                        "        },\n"
+                        "        \"dict3\": {\n"
+                        "            \"userdef\": {\n"
+                        "                \"integer\": 42,\n"
+                        "                \"string\": \"user def string\"\n"
+                        "            },\n"
+                        "            \"string\": \"forty five\"\n"
+                        "        }\n"
+                        "    }\n"
+                        "}");
+    } else {
+        g_assert_cmpstr(out, ==,
+                        "{"
+                         "\"string0\": \"forty two\", "
+                         "\"dict1\": {"
+                          "\"string1\": \"forty three\", "
+                          "\"dict2\": {"
+                           "\"userdef\": {"
+                            "\"integer\": 42, "
+                            "\"string\": \"user def string\""
+                            "}, "
+                           "\"string\": \"forty four\""
+                           "}, "
+                          "\"dict3\": {"
+                           "\"userdef\": {"
+                            "\"integer\": 42, "
+                            "\"string\": \"user def string\""
+                            "}, "
+                          "\"string\": \"forty five\""
+                          "}"
+                         "}"
+                        "}");
+    }
     qapi_free_UserDefTwo(ud2);
 }

@@ -346,36 +375,49 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
     g_assert_cmpstr(out, ==, "null");
 }

-static void output_visitor_test_add(const char *testpath,
+static void output_visitor_test_add(const char *testpath, bool *data,
                                     void (*test_func)(TestOutputVisitorData *,
                                                       const void *))
 {
-    g_test_add(testpath, TestOutputVisitorData, NULL, visitor_output_setup,
+    g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
                test_func, visitor_output_teardown);
 }

 int main(int argc, char **argv)
 {
+    bool plain = false;
+    bool pretty = true;
+
     g_test_init(&argc, &argv, NULL);

-    output_visitor_test_add("/visitor/json/int", test_visitor_out_int);
-    output_visitor_test_add("/visitor/json/bool", test_visitor_out_bool);
-    output_visitor_test_add("/visitor/json/number", test_visitor_out_number);
-    output_visitor_test_add("/visitor/json/string", test_visitor_out_string);
-    output_visitor_test_add("/visitor/json/enum", test_visitor_out_enum);
-    output_visitor_test_add("/visitor/json/enum-errors",
+    output_visitor_test_add("/visitor/json/int", &plain,
+                            test_visitor_out_int);
+    output_visitor_test_add("/visitor/json/bool", &plain,
+                            test_visitor_out_bool);
+    output_visitor_test_add("/visitor/json/number", &plain,
+                            test_visitor_out_number);
+    output_visitor_test_add("/visitor/json/string", &plain,
+                            test_visitor_out_string);
+    output_visitor_test_add("/visitor/json/enum", &plain,
+                            test_visitor_out_enum);
+    output_visitor_test_add("/visitor/json/enum-errors", &plain,
                             test_visitor_out_enum_errors);
-    output_visitor_test_add("/visitor/json/struct", test_visitor_out_struct);
-    output_visitor_test_add("/visitor/json/struct-nested",
+    output_visitor_test_add("/visitor/json/struct", &plain,
+                            test_visitor_out_struct);
+    output_visitor_test_add("/visitor/json/struct-nested", &plain,
                             test_visitor_out_struct_nested);
-    output_visitor_test_add("/visitor/json/struct-errors",
+    output_visitor_test_add("/visitor/json-pretty/struct-nested", &pretty,
+                            test_visitor_out_struct_nested);
+    output_visitor_test_add("/visitor/json/struct-errors", &plain,
                             test_visitor_out_struct_errors);
-    output_visitor_test_add("/visitor/json/list", test_visitor_out_list);
-    output_visitor_test_add("/visitor/json/union-flat",
+    output_visitor_test_add("/visitor/json/list", &plain,
+                            test_visitor_out_list);
+    output_visitor_test_add("/visitor/json/union-flat", &plain,
                             test_visitor_out_union_flat);
-    output_visitor_test_add("/visitor/json/alternate",
+    output_visitor_test_add("/visitor/json/alternate", &plain,
                             test_visitor_out_alternate);
-    output_visitor_test_add("/visitor/json/null", test_visitor_out_null);
+    output_visitor_test_add("/visitor/json/null", &plain,
+                            test_visitor_out_null);

     g_test_run();

-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (24 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in " Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-03  8:25   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output Eric Blake
                   ` (5 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Luiz Capitulino

Rather than open-code two different JSON visitors, it's nicer to
make qobject_to_json() reuse the JSON output visitor.  This also
lets us pass QObject to any output visitor (passing it to the
string output visitor will fail if structs are involved, but
passing it to the QMP output visitor would provide a way to
deep-clone a QObject).

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

---
v4: new patch, inspired by discussion on v3 12/18
---
 include/qapi/qmp/qobject-json.h |   9 ++++
 qobject/qobject-json.c          | 108 +++++++++++-----------------------------
 2 files changed, 39 insertions(+), 78 deletions(-)

diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index e4d11cf..28e44b2 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -24,6 +24,15 @@ QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0);
 /* Convert the object to QString; does not fail. */
 QString *qobject_to_json(const QObject *obj, bool pretty);

+/*
+ * Visit the object with an output visitor @v.
+ *
+ * @name represents the relationship of this object to any parent
+ * (ignored for a top-level visit, must be set if part of a
+ * dictionary, should be NULL if part of a list).
+ */
+void qobject_visit_output(Visitor *v, const char *name, const QObject *obj);
+
 int qstring_append_json_string(QString *qstring, const char *str);
 int qstring_append_json_number(QString *qstring, double number);

diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 9ace92b..05b020a 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -18,6 +18,9 @@
 #include "qapi/qmp/qobject-json.h"
 #include "qemu/unicode.h"
 #include "qapi/qmp/types.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qapi/json-output-visitor.h"
 #include <math.h>

 typedef struct JSONParsingState
@@ -69,115 +72,62 @@ QObject *qobject_from_jsonf(const char *string, ...)
     return obj;
 }

-typedef struct ToJsonIterState
-{
-    int indent;
-    bool pretty;
-    int count;
-    QString *str;
-} ToJsonIterState;
-
-static void to_json(const QObject *obj, QString *str, bool pretty, int indent);
-
 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
-    ToJsonIterState *s = opaque;
+    Visitor *v = opaque;

-    if (s->count) {
-        qstring_append(s->str, s->pretty ? "," : ", ");
-    }
-
-    if (s->pretty) {
-        qstring_append_printf(s->str, "\n%*s", 4 * s->indent, "");
-    }
-
-    qstring_append_json_string(s->str, key);
-
-    qstring_append(s->str, ": ");
-    to_json(obj, s->str, s->pretty, s->indent);
-    s->count++;
+    qobject_visit_output(v, key, obj);
 }

 static void to_json_list_iter(QObject *obj, void *opaque)
 {
-    ToJsonIterState *s = opaque;
+    Visitor *v = opaque;

-    if (s->count) {
-        qstring_append(s->str, s->pretty ? "," : ", ");
-    }
-
-    if (s->pretty) {
-        qstring_append_printf(s->str, "\n%*s", 4 * s->indent, "");
-    }
-
-    to_json(obj, s->str, s->pretty, s->indent);
-    s->count++;
+    qobject_visit_output(v, NULL, obj);
 }

-static void to_json(const QObject *obj, QString *str, bool pretty, int indent)
+void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
 {
     switch (qobject_type(obj)) {
     case QTYPE_QNULL:
-        qstring_append(str, "null");
+        visit_type_null(v, name, &error_abort);
         break;
     case QTYPE_QINT: {
-        QInt *val = qobject_to_qint(obj);
-        qstring_append_printf(str, "%" PRId64, qint_get_int(val));
+        int64_t val = qint_get_int(qobject_to_qint(obj));
+        visit_type_int64(v, name, &val, &error_abort);
         break;
     }
     case QTYPE_QSTRING: {
-        QString *val = qobject_to_qstring(obj);
+        const char *str = qstring_get_str(qobject_to_qstring(obj));
         /* FIXME: no way inform user if we modified the string to
          * avoid encoding errors */
-        qstring_append_json_string(str, qstring_get_str(val));
+        visit_type_str(v, name, (char **)&str, &error_abort);
         break;
     }
     case QTYPE_QDICT: {
-        ToJsonIterState s;
         QDict *val = qobject_to_qdict(obj);
-
-        s.count = 0;
-        s.str = str;
-        s.indent = indent + 1;
-        s.pretty = pretty;
-        qstring_append_chr(str, '{');
-        qdict_iter(val, to_json_dict_iter, &s);
-        if (pretty) {
-            qstring_append_printf(str, "\n%*s", 4 * indent, "");
-        }
-        qstring_append_chr(str, '}');
+        visit_start_struct(v, name, NULL, 0, &error_abort);
+        qdict_iter(val, to_json_dict_iter, v);
+        visit_check_struct(v, &error_abort);
+        visit_end_struct(v, NULL);
         break;
     }
     case QTYPE_QLIST: {
-        ToJsonIterState s;
         QList *val = qobject_to_qlist(obj);
-
-        s.count = 0;
-        s.str = str;
-        s.indent = indent + 1;
-        s.pretty = pretty;
-        qstring_append_chr(str, '[');
-        qlist_iter(val, (void *)to_json_list_iter, &s);
-        if (pretty) {
-            qstring_append_printf(str, "\n%*s", 4 * indent, "");
-        }
-        qstring_append_chr(str, ']');
+        visit_start_list(v, name, NULL, 0, &error_abort);
+        qlist_iter(val, to_json_list_iter, v);
+        visit_end_list(v, NULL);
         break;
     }
     case QTYPE_QFLOAT: {
-        QFloat *val = qobject_to_qfloat(obj);
+        double val = qfloat_get_double(qobject_to_qfloat(obj));
         /* FIXME: no way inform user if we generated invalid JSON */
-        qstring_append_json_number(str, qfloat_get_double(val));
+        visit_type_number(v, name, &val, NULL);
         break;
     }
     case QTYPE_QBOOL: {
-        QBool *val = qobject_to_qbool(obj);
-
-        if (qbool_get_bool(val)) {
-            qstring_append(str, "true");
-        } else {
-            qstring_append(str, "false");
-        }
+        bool val = qbool_get_bool(qobject_to_qbool(obj));
+        visit_type_bool(v, name, &val, &error_abort);
         break;
     }
     default:
@@ -187,11 +137,13 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent)

 QString *qobject_to_json(const QObject *obj, bool pretty)
 {
-    QString *str = qstring_new();
+    char *str;
+    Visitor *v = json_output_visitor_new(pretty, &str);

-    to_json(obj, str, pretty, 0);
-
-    return str;
+    qobject_visit_output(v, NULL, obj);
+    visit_complete(v, &str);
+    visit_free(v);
+    return qstring_wrap_str(str);
 }

 /**
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (25 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-06-03  8:29   ` Markus Armbruster
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 28/28] qemu-img: Use new JSON output formatter Eric Blake
                   ` (4 subsequent siblings)
  31 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Michael Roth

Now that we can visit any QObject, it's easy to add support
for visit_type_any() in the JSON output visitor.

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

---
v4: new patch, split out of v3 7/18, but made simpler by
not requiring v3 12/18
---
 include/qapi/json-output-visitor.h |  2 --
 qapi/json-output-visitor.c         |  7 ++++++
 tests/test-json-output-visitor.c   | 50 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
index 94c9e0f..414f91b 100644
--- a/include/qapi/json-output-visitor.h
+++ b/include/qapi/json-output-visitor.h
@@ -23,8 +23,6 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
  *
  * If @pretty, make the output legible with newlines and indentation;
  * otherwise the output uses a single line.
- *
- * For now, this cannot be used to visit the 'any' type.
  */
 Visitor *json_output_visitor_new(bool pretty, char **result);

diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
index 881c9ee..ba647d3 100644
--- a/qapi/json-output-visitor.c
+++ b/qapi/json-output-visitor.c
@@ -147,6 +147,12 @@ static void json_output_type_number(Visitor *v, const char *name, double *obj,
     qstring_append_json_number(jov->str, *obj);
 }

+static void json_output_type_any(Visitor *v, const char *name, QObject **obj,
+                                 Error **errp)
+{
+    qobject_visit_output(v, name, *obj);
+}
+
 static void json_output_type_null(Visitor *v, const char *name, Error **errp)
 {
     JsonOutputVisitor *jov = to_jov(v);
@@ -196,6 +202,7 @@ Visitor *json_output_visitor_new(bool pretty, char **result)
     v->visitor.type_bool = json_output_type_bool;
     v->visitor.type_str = json_output_type_str;
     v->visitor.type_number = json_output_type_number;
+    v->visitor.type_any = json_output_type_any;
     v->visitor.type_null = json_output_type_null;
     v->visitor.complete = json_output_complete;
     v->visitor.free = json_output_free;
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
index 5073715..fd14e26 100644
--- a/tests/test-json-output-visitor.c
+++ b/tests/test-json-output-visitor.c
@@ -316,6 +316,52 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
     qapi_free_TestStructList(head);
 }

+static void test_visitor_out_any(TestOutputVisitorData *data,
+                                 const void *arg)
+{
+    const bool *pretty = arg;
+    QObject *qobj;
+    QDict *qdict;
+    const char *out;
+
+    qobj = QOBJECT(qint_from_int(-42));
+    visit_type_any(data->ov, NULL, &qobj, &error_abort);
+    out = visitor_get(data);
+    g_assert_cmpstr(out, ==, "-42");
+    qobject_decref(qobj);
+
+    visitor_reset(data);
+    qdict = qdict_new();
+    /* Ordering here is sensitive to the hashing chosen by QDict. */
+    qdict_put(qdict, "integer", qint_from_int(-42));
+    qdict_put(qdict, "list", qlist_new());
+    qdict_put(qdict, "boolean", qbool_from_bool(true));
+    qdict_put(qdict, "string", qstring_from_str("foo"));
+    qobj = QOBJECT(qdict);
+    visit_type_any(data->ov, NULL, &qobj, &error_abort);
+    qobject_decref(qobj);
+    out = visitor_get(data);
+
+    if (*pretty) {
+        g_assert_cmpstr(out, ==,
+                        "{\n"
+                        "    \"integer\": -42,\n"
+                        "    \"list\": [\n"
+                        "    ],\n"
+                        "    \"boolean\": true,\n"
+                        "    \"string\": \"foo\"\n"
+                        "}");
+    } else {
+        g_assert_cmpstr(out, ==,
+                        "{"
+                         "\"integer\": -42, "
+                         "\"list\": [], "
+                         "\"boolean\": true, "
+                         "\"string\": \"foo\""
+                        "}");
+    }
+}
+
 static void test_visitor_out_union_flat(TestOutputVisitorData *data,
                                         const void *unused)
 {
@@ -412,6 +458,10 @@ int main(int argc, char **argv)
                             test_visitor_out_struct_errors);
     output_visitor_test_add("/visitor/json/list", &plain,
                             test_visitor_out_list);
+    output_visitor_test_add("/visitor/json/any", &plain,
+                            test_visitor_out_any);
+    output_visitor_test_add("/visitor/json-pretty/any", &pretty,
+                            test_visitor_out_any);
     output_visitor_test_add("/visitor/json/union-flat", &plain,
                             test_visitor_out_union_flat);
     output_visitor_test_add("/visitor/json/alternate", &plain,
-- 
2.5.5

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

* [Qemu-devel] [PATCH v4 28/28] qemu-img: Use new JSON output formatter
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (26 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output Eric Blake
@ 2016-05-19  4:41 ` Eric Blake
  2016-05-19 14:58 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19  4:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Kevin Wolf, Max Reitz, open list:Block layer core

Now that we can pretty-print straight to JSON from a visitor,
we can eliminate the temporary conversion into QObject inside
qemu-img.

The changes to qemu-iotests 043 expected output demonstrates
the fact that output is now done in qapi declaration order,
rather than QDict hash order.

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

---
v4: rebase to earlier changes
v3: rebase to master
v2: Drop RFC, adjust expected output of 043; rebase to 'name' motion

Overlaps with Fam's qemu-img edits, although he has expressed
interest in getting this one in first.
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01756.html
https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg01806.html
---
 qemu-img.c                 | 43 +++++++++++++++++--------------------------
 tests/qemu-iotests/043.out | 22 +++++++++++-----------
 2 files changed, 28 insertions(+), 37 deletions(-)

diff --git a/qemu-img.c b/qemu-img.c
index 086530f..d5dc19b 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -24,9 +24,9 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qapi-visit.h"
-#include "qapi/qmp-output-visitor.h"
+#include "qapi/json-output-visitor.h"
 #include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qobject-json.h"
+#include "qapi/qmp/types.h"
 #include "qemu/cutils.h"
 #include "qemu/config-file.h"
 #include "qemu/option.h"
@@ -479,17 +479,14 @@ fail:

 static void dump_json_image_check(ImageCheck *check, bool quiet)
 {
-    QString *str;
-    QObject *obj;
-    Visitor *v = qmp_output_visitor_new(&obj);
+    char *str;
+    Visitor *v = json_output_visitor_new(true, &str);

     visit_type_ImageCheck(v, NULL, &check, &error_abort);
-    visit_complete(v, &obj);
-    str = qobject_to_json(obj, true);
-    qprintf(quiet, "%s\n", qstring_get_str(str));
-    qobject_decref(obj);
+    visit_complete(v, &str);
+    qprintf(quiet, "%s\n", str);
+    g_free(str);
     visit_free(v);
-    QDECREF(str);
 }

 static void dump_human_image_check(ImageCheck *check, bool quiet)
@@ -2168,32 +2165,26 @@ static void dump_snapshots(BlockDriverState *bs)

 static void dump_json_image_info_list(ImageInfoList *list)
 {
-    QString *str;
-    QObject *obj;
-    Visitor *v = qmp_output_visitor_new(&obj);
+    char *str;
+    Visitor *v = json_output_visitor_new(true, &str);

     visit_type_ImageInfoList(v, NULL, &list, &error_abort);
-    visit_complete(v, &obj);
-    str = qobject_to_json(obj, true);
-    printf("%s\n", qstring_get_str(str));
-    qobject_decref(obj);
+    visit_complete(v, &str);
+    printf("%s\n", str);
     visit_free(v);
-    QDECREF(str);
+    g_free(str);
 }

 static void dump_json_image_info(ImageInfo *info)
 {
-    QString *str;
-    QObject *obj;
-    Visitor *v = qmp_output_visitor_new(&obj);
+    char *str;
+    Visitor *v = json_output_visitor_new(true, &str);

     visit_type_ImageInfo(v, NULL, &info, &error_abort);
-    visit_complete(v, &obj);
-    str = qobject_to_json(obj, true);
-    printf("%s\n", qstring_get_str(str));
-    qobject_decref(obj);
+    visit_complete(v, &str);
+    printf("%s\n", str);
     visit_free(v);
-    QDECREF(str);
+    g_free(str);
 }

 static void dump_human_image_info_list(ImageInfoList *list)
diff --git a/tests/qemu-iotests/043.out b/tests/qemu-iotests/043.out
index b37d2a3..41697a2 100644
--- a/tests/qemu-iotests/043.out
+++ b/tests/qemu-iotests/043.out
@@ -40,29 +40,29 @@ cluster_size: 65536
 == finite chain of length 3 (json) ==
 [
     {
-        "virtual-size": 134217728,
         "filename": "TEST_DIR/t.IMGFMT",
-        "cluster-size": 65536,
         "format": "IMGFMT",
-        "full-backing-filename": "TEST_DIR/t.IMGFMT.2.base",
+        "dirty-flag": false,
+        "virtual-size": 134217728,
+        "cluster-size": 65536,
         "backing-filename": "TEST_DIR/t.IMGFMT.2.base",
-        "dirty-flag": false
+        "full-backing-filename": "TEST_DIR/t.IMGFMT.2.base",
     },
     {
-        "virtual-size": 134217728,
         "filename": "TEST_DIR/t.IMGFMT.2.base",
-        "cluster-size": 65536,
         "format": "IMGFMT",
-        "full-backing-filename": "TEST_DIR/t.IMGFMT.1.base",
+        "dirty-flag": false,
+        "virtual-size": 134217728,
+        "cluster-size": 65536,
         "backing-filename": "TEST_DIR/t.IMGFMT.1.base",
-        "dirty-flag": false
+        "full-backing-filename": "TEST_DIR/t.IMGFMT.1.base",
     },
     {
-        "virtual-size": 134217728,
         "filename": "TEST_DIR/t.IMGFMT.1.base",
-        "cluster-size": 65536,
         "format": "IMGFMT",
-        "dirty-flag": false
+        "dirty-flag": false,
+        "virtual-size": 134217728,
+        "cluster-size": 65536,
     }
 ]
 *** done
-- 
2.5.5

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

* Re: [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (27 preceding siblings ...)
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 28/28] qemu-img: Use new JSON output formatter Eric Blake
@ 2016-05-19 14:58 ` Eric Blake
  2016-05-19 16:52 ` [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor Eric Blake
                   ` (2 subsequent siblings)
  31 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19 14:58 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru

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

On 05/18/2016 10:40 PM, Eric Blake wrote:
> Hard prerequisites (for all patches to apply):
> Markus' qjson movement:
> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg00471.html
> 
> Soft prerequisites (for valgrind to be happy with all touched tests):
> My fix for memleak in range.h:
> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg02666.html
> My fix for memleak in json-streamer.c:
> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg03186.html
> 

Forgot to mention: available as a tag at:
git fetch git://repo.or.cz/qemu/ericb.git qapi-jsonv4

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

* [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (28 preceding siblings ...)
  2016-05-19 14:58 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
@ 2016-05-19 16:52 ` Eric Blake
  2016-05-19 20:18   ` Eric Blake
  2016-06-03  9:21   ` Markus Armbruster
  2016-05-19 17:05 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Markus Armbruster
  2016-06-03 12:09 ` Markus Armbruster
  31 siblings, 2 replies; 73+ messages in thread
From: Eric Blake @ 2016-05-19 16:52 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Michael Roth, Luiz Capitulino, Kevin Wolf, Max Reitz,
	open list:Block layer core

Let the caller decide whether output must be strict JSON (and
raise an error on an attempt to output an encoding error or
non-finite number), vs. the status quo of relaxed (encoding
errors are rewritten to use substitute U+fffd characters,
and non-finite numbers are output).

Adjust the testsuite to cover this: check-qobject-json checks
relaxed mode (since qobject_to_json() is unchanged in behavior),
test-qmp-output-visitor checks that QObject doesn't care about
JSON restrictions, and test-json-output-visitor is now in
strict mode and flags the errors.

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

---
v4: split out as new patch
[was parts of 04/18 and 07/18 in v3]
---
 include/qapi/json-output-visitor.h |  7 ++++-
 include/qapi/qmp/qobject-json.h    |  3 +-
 qapi/json-output-visitor.c         | 19 ++++++++----
 qemu-img.c                         |  6 ++--
 qobject/qobject-json.c             | 61 ++++++++++++++++++++++++--------------
 tests/check-qobject-json.c         | 15 ++++++++--
 tests/test-json-output-visitor.c   | 17 +++++++++--
 tests/test-qmp-output-visitor.c    | 17 +++++++++++
 8 files changed, 107 insertions(+), 38 deletions(-)

diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
index 414f91b..2ce2758 100644
--- a/include/qapi/json-output-visitor.h
+++ b/include/qapi/json-output-visitor.h
@@ -23,7 +23,12 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
  *
  * If @pretty, make the output legible with newlines and indentation;
  * otherwise the output uses a single line.
+ *
+ * If @strict, attempts to output invalid JSON (strings not properly
+ * encoded in UTF-8, or Infinity or NaN as a number) will be treated
+ * as an error; otherwise, output is best-effort even if not pure
+ * JSON.
  */
-Visitor *json_output_visitor_new(bool pretty, char **result);
+Visitor *json_output_visitor_new(bool pretty, bool strict, char **result);

 #endif
diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
index 28e44b2..30bc753 100644
--- a/include/qapi/qmp/qobject-json.h
+++ b/include/qapi/qmp/qobject-json.h
@@ -31,7 +31,8 @@ QString *qobject_to_json(const QObject *obj, bool pretty);
  * (ignored for a top-level visit, must be set if part of a
  * dictionary, should be NULL if part of a list).
  */
-void qobject_visit_output(Visitor *v, const char *name, const QObject *obj);
+void qobject_visit_output(Visitor *v, const char *name, const QObject *obj,
+                          Error **errp);

 int qstring_append_json_string(QString *qstring, const char *str);
 int qstring_append_json_number(QString *qstring, double number);
diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
index ba647d3..28e95ee 100644
--- a/qapi/json-output-visitor.c
+++ b/qapi/json-output-visitor.c
@@ -13,11 +13,13 @@
 #include "qapi/visitor-impl.h"
 #include "qapi/qmp/qstring.h"
 #include "qapi/qmp/qobject-json.h"
+#include "qapi/error.h"

 struct JsonOutputVisitor {
     Visitor visitor;
     QString *str;
     bool pretty;
+    bool strict;
     bool comma;
     unsigned int depth;
     char **result;
@@ -133,8 +135,10 @@ static void json_output_type_str(Visitor *v, const char *name, char **obj,

     assert(*obj);
     json_output_name(jov, name);
-    /* FIXME: report invalid UTF-8 encoding */
-    qstring_append_json_string(jov->str, *obj);
+    if (qstring_append_json_string(jov->str, *obj) < 0 && jov->strict) {
+        error_setg(errp, "string value of '%s' is not valid UTF-8",
+                   name ? name : "null");
+    }
 }

 static void json_output_type_number(Visitor *v, const char *name, double *obj,
@@ -143,14 +147,16 @@ static void json_output_type_number(Visitor *v, const char *name, double *obj,
     JsonOutputVisitor *jov = to_jov(v);

     json_output_name(jov, name);
-    /* FIXME: report Inf/NaN problems */
-    qstring_append_json_number(jov->str, *obj);
+    if (qstring_append_json_number(jov->str, *obj) < 0 && jov->strict) {
+        error_setg(errp, "number value of '%s' is not finite",
+                   name ? name : "null");
+    }
 }

 static void json_output_type_any(Visitor *v, const char *name, QObject **obj,
                                  Error **errp)
 {
-    qobject_visit_output(v, name, *obj);
+    qobject_visit_output(v, name, *obj, errp);
 }

 static void json_output_type_null(Visitor *v, const char *name, Error **errp)
@@ -181,12 +187,13 @@ static void json_output_free(Visitor *v)
     g_free(jov);
 }

-Visitor *json_output_visitor_new(bool pretty, char **result)
+Visitor *json_output_visitor_new(bool pretty, bool strict, char **result)
 {
     JsonOutputVisitor *v;

     v = g_malloc0(sizeof(*v));
     v->pretty = pretty;
+    v->strict = strict;
     v->result = result;
     *result = NULL;
     v->str = qstring_new();
diff --git a/qemu-img.c b/qemu-img.c
index d5dc19b..b0c6dd3 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -480,7 +480,7 @@ fail:
 static void dump_json_image_check(ImageCheck *check, bool quiet)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(true, &str);
+    Visitor *v = json_output_visitor_new(true, true, &str);

     visit_type_ImageCheck(v, NULL, &check, &error_abort);
     visit_complete(v, &str);
@@ -2166,7 +2166,7 @@ static void dump_snapshots(BlockDriverState *bs)
 static void dump_json_image_info_list(ImageInfoList *list)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(true, &str);
+    Visitor *v = json_output_visitor_new(true, true, &str);

     visit_type_ImageInfoList(v, NULL, &list, &error_abort);
     visit_complete(v, &str);
@@ -2178,7 +2178,7 @@ static void dump_json_image_info_list(ImageInfoList *list)
 static void dump_json_image_info(ImageInfo *info)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(true, &str);
+    Visitor *v = json_output_visitor_new(true, true, &str);

     visit_type_ImageInfo(v, NULL, &info, &error_abort);
     visit_complete(v, &str);
diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
index 05b020a..cf81ab5 100644
--- a/qobject/qobject-json.c
+++ b/qobject/qobject-json.c
@@ -72,62 +72,81 @@ QObject *qobject_from_jsonf(const char *string, ...)
     return obj;
 }

+typedef struct ToJson {
+{
+    Visitor *v;
+    Error **errp;
+} ToJson;
+
 static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
 {
-    Visitor *v = opaque;
+    ToJson *s = opaque;

-    qobject_visit_output(v, key, obj);
+    if (!*s->errp) {
+        qobject_visit_output(s->v, key, obj, s->errp);
+    }
 }

 static void to_json_list_iter(QObject *obj, void *opaque)
 {
-    Visitor *v = opaque;
+    ToJson *s = opaque;

-    qobject_visit_output(v, NULL, obj);
+    if (!*s->errp) {
+        qobject_visit_output(s->v, NULL, obj, s->errp);
+    }
 }

-void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
+void qobject_visit_output(Visitor *v, const char *name, const QObject *obj,
+                          Error **errp)
 {
+    Error *err = NULL;
+    ToJson s = { .v = v, .errp = &err, };
+
     switch (qobject_type(obj)) {
     case QTYPE_QNULL:
-        visit_type_null(v, name, &error_abort);
+        visit_type_null(v, name, errp);
         break;
     case QTYPE_QINT: {
         int64_t val = qint_get_int(qobject_to_qint(obj));
-        visit_type_int64(v, name, &val, &error_abort);
+        visit_type_int64(v, name, &val, errp);
         break;
     }
     case QTYPE_QSTRING: {
         const char *str = qstring_get_str(qobject_to_qstring(obj));
-        /* FIXME: no way inform user if we modified the string to
-         * avoid encoding errors */
-        visit_type_str(v, name, (char **)&str, &error_abort);
+        visit_type_str(v, name, (char **)&str, errp);
         break;
     }
     case QTYPE_QDICT: {
         QDict *val = qobject_to_qdict(obj);
-        visit_start_struct(v, name, NULL, 0, &error_abort);
-        qdict_iter(val, to_json_dict_iter, v);
-        visit_check_struct(v, &error_abort);
+        visit_start_struct(v, name, NULL, 0, &err);
+        if (!err) {
+            qdict_iter(val, to_json_dict_iter, &s);
+        }
+        if (!err) {
+            visit_check_struct(v, &err);
+        }
+        error_propagate(errp, err);
         visit_end_struct(v, NULL);
         break;
     }
     case QTYPE_QLIST: {
         QList *val = qobject_to_qlist(obj);
-        visit_start_list(v, name, NULL, 0, &error_abort);
-        qlist_iter(val, to_json_list_iter, v);
+        visit_start_list(v, name, NULL, 0, &err);
+        if (!err) {
+            qlist_iter(val, to_json_list_iter, &s);
+        }
+        error_propagate(errp, err);
         visit_end_list(v, NULL);
         break;
     }
     case QTYPE_QFLOAT: {
         double val = qfloat_get_double(qobject_to_qfloat(obj));
-        /* FIXME: no way inform user if we generated invalid JSON */
-        visit_type_number(v, name, &val, NULL);
+        visit_type_number(v, name, &val, errp);
         break;
     }
     case QTYPE_QBOOL: {
         bool val = qbool_get_bool(qobject_to_qbool(obj));
-        visit_type_bool(v, name, &val, &error_abort);
+        visit_type_bool(v, name, &val, errp);
         break;
     }
     default:
@@ -138,9 +157,9 @@ void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
 QString *qobject_to_json(const QObject *obj, bool pretty)
 {
     char *str;
-    Visitor *v = json_output_visitor_new(pretty, &str);
+    Visitor *v = json_output_visitor_new(pretty, false, &str);

-    qobject_visit_output(v, NULL, obj);
+    qobject_visit_output(v, NULL, obj, &error_abort);
     visit_complete(v, &str);
     visit_free(v);
     return qstring_wrap_str(str);
@@ -222,8 +241,6 @@ int qstring_append_json_number(QString *qstring, double number)
     /* FIXME: snprintf() is locale dependent; but JSON requires
      * numbers to be formatted as if in the C locale. Dependence
      * on C locale is a pervasive issue in QEMU. */
-    /* FIXME: This risks printing Inf or NaN, which are not valid
-     * JSON values. */
     /* FIXME: the default precision of 6 for %f often causes
      * rounding errors; we should be using DBL_DECIMAL_DIG (17),
      * and only rounding to a shorter number if the result would
diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
index 267fc67..b4f3dc4 100644
--- a/tests/check-qobject-json.c
+++ b/tests/check-qobject-json.c
@@ -12,6 +12,7 @@
  */
 #include "qemu/osdep.h"
 #include <glib.h>
+#include <math.h>

 #include "qapi/qmp/types.h"
 #include "qapi/qmp/qobject-json.h"
@@ -920,6 +921,8 @@ static void simple_number(void)
 static void float_number(void)
 {
     int i;
+    QFloat *qfloat;
+    QString *str;
     struct {
         const char *encoded;
         double decoded;
@@ -934,7 +937,6 @@ static void float_number(void)

     for (i = 0; test_cases[i].encoded; i++) {
         QObject *obj;
-        QFloat *qfloat;

         obj = qobject_from_json(test_cases[i].encoded);
         g_assert(obj != NULL);
@@ -944,8 +946,6 @@ static void float_number(void)
         g_assert(qfloat_get_double(qfloat) == test_cases[i].decoded);

         if (test_cases[i].skip == 0) {
-            QString *str;
-
             str = qobject_to_json(obj, false);
             g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
             QDECREF(str);
@@ -953,6 +953,15 @@ static void float_number(void)

         QDECREF(qfloat);
     }
+
+    /* Although Infinity/NaN are not valid JSON, we can generate them
+     * on output as an extension.
+     * FIXME: The parser doesn't handle infinity/NaN on input. */
+    qfloat = qfloat_from_double(INFINITY);
+    str = qobject_to_json(QOBJECT(qfloat), false);
+    g_assert_cmpstr(qstring_get_str(str), ==, "inf");
+    QDECREF(str);
+    QDECREF(qfloat);
 }

 static void vararg_number(void)
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
index fd14e26..e0478bc 100644
--- a/tests/test-json-output-visitor.c
+++ b/tests/test-json-output-visitor.c
@@ -14,6 +14,7 @@

 #include "qemu/osdep.h"
 #include <glib.h>
+#include <math.h>

 #include "qemu-common.h"
 #include "qapi/json-output-visitor.h"
@@ -32,7 +33,7 @@ static void visitor_output_setup(TestOutputVisitorData *data,
                                  const void *arg)
 {
     const bool *pretty = arg;
-    data->ov = json_output_visitor_new(*pretty, &data->str);
+    data->ov = json_output_visitor_new(*pretty, true, &data->str);
     g_assert(data->ov);
     data->pretty = *pretty;
 }
@@ -89,13 +90,18 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
 {
     double value = 3.14;
     const char *out;
+    Error *err = NULL;

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

     out = visitor_get(data);
     g_assert_cmpstr(out, ==, "3.14");

-    /* FIXME: JSON requires finite numbers */
+    /* JSON requires finite numbers */
+    visitor_reset(data);
+    value = INFINITY;
+    visit_type_number(data->ov, NULL, &value, &err);
+    error_free_or_abort(&err);
 }

 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -103,11 +109,18 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
 {
     char *string = (char *) "Q E M U";
     const char *out;
+    Error *err = NULL;

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

     out = visitor_get(data);
     g_assert_cmpstr(out, ==, "\"Q E M U\"");
+
+    /* JSON requires valid UTF-8 encodings */
+    visitor_reset(data);
+    string = (char *) "\xfe";
+    visit_type_str(data->ov, NULL, &string, &err);
+    error_free_or_abort(&err);
 }

 static void test_visitor_out_enum(TestOutputVisitorData *data,
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 0db0b19..68e074f 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -17,6 +17,7 @@

 #include "qemu/osdep.h"
 #include <glib.h>
+#include <math.h>

 #include "qemu-common.h"
 #include "qapi/error.h"
@@ -97,6 +98,14 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
     obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QFLOAT);
     g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
+
+    /* While JSON requires finite values, QObject does not. */
+    visitor_reset(data);
+    value = INFINITY;
+    visit_type_number(data->ov, NULL, &value, &error_abort);
+    obj = visitor_get(data);
+    g_assert(qobject_type(obj) == QTYPE_QFLOAT);
+    g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == INFINITY);
 }

 static void test_visitor_out_string(TestOutputVisitorData *data,
@@ -110,6 +119,14 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
     obj = visitor_get(data);
     g_assert(qobject_type(obj) == QTYPE_QSTRING);
     g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
+
+    /* While JSON requires valid UTF-8 encoding, QObject does not. */
+    visitor_reset(data);
+    string = (char *) "\xfe";
+    visit_type_str(data->ov, NULL, &string, &error_abort);
+    obj = visitor_get(data);
+    g_assert(qobject_type(obj) == QTYPE_QSTRING);
+    g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
 }

 static void test_visitor_out_no_string(TestOutputVisitorData *data,
-- 
2.5.5

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

* Re: [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (29 preceding siblings ...)
  2016-05-19 16:52 ` [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor Eric Blake
@ 2016-05-19 17:05 ` Markus Armbruster
  2016-06-03 12:09 ` Markus Armbruster
  31 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-05-19 17:05 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Meant to inquire about the next batch, but of course you beat me to the
punch :)

I'll be on vacation next week, so review will be delayed.

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

* Re: [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor
  2016-05-19 16:52 ` [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor Eric Blake
@ 2016-05-19 20:18   ` Eric Blake
  2016-06-03  8:36     ` Markus Armbruster
  2016-06-03  9:21   ` Markus Armbruster
  1 sibling, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-05-19 20:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Kevin Wolf, Michael Roth, open list:Block layer core, armbru,
	Luiz Capitulino, Max Reitz

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

On 05/19/2016 10:52 AM, Eric Blake wrote:
> Let the caller decide whether output must be strict JSON (and
> raise an error on an attempt to output an encoding error or
> non-finite number), vs. the status quo of relaxed (encoding
> errors are rewritten to use substitute U+fffd characters,
> and non-finite numbers are output).
> 
> Adjust the testsuite to cover this: check-qobject-json checks
> relaxed mode (since qobject_to_json() is unchanged in behavior),
> test-qmp-output-visitor checks that QObject doesn't care about
> JSON restrictions, and test-json-output-visitor is now in
> strict mode and flags the errors.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> 

> +++ b/qobject/qobject-json.c
> @@ -72,62 +72,81 @@ QObject *qobject_from_jsonf(const char *string, ...)
>      return obj;
>  }
> 
> +typedef struct ToJson {
> +{
> +    Visitor *v;

Uggh, posted the wrong version. checkpatch complained the { was on the
wrong line, so I added it in the right one and then pushed before
re-saving after deleting the duplicate one.

Actually, if this patch is worthwhile, I'm thinking that in v5, I'll
minimize some of the churn and keep the ToJsonIterState struct that I
removed in 26/28, even if it only has a single 'Visitor *v' member at
that time, rather than completely removing it there to reinstate it here.

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

* Re: [Qemu-devel] [PATCH v4 01/28] qapi: Rename (one) qjson.h to qobject-json.h
  2016-05-19  4:40   ` [Qemu-devel] " Eric Blake
@ 2016-06-01 15:09     ` Markus Armbruster
  -1 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 15:09 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, Alexander Graf,
	open list:Block layer core, Alberto Garcia, Michael Roth,
	Chrysostomos Nanakos, Michael S. Tsirkin, Jeff Cody, Max Reitz,
	Christian Borntraeger, Gerd Hoffmann, open list:Overall,
	Cornelia Huck, Paolo Bonzini, Luiz Capitulino, Jason Wang,
	Richard Henderson

Eric Blake <eblake@redhat.com> writes:

> We have two different JSON visitors in the tree; and having both
> named 'qjson.h' can cause include confusion.  Rename the qapi
> version.

Commit 17b74b9 moved the other one to migration/.  If you still want to
rename this one, you'll need to update the commit message.

> Why did I pick that one?  A later patch plans on deleting the
> top-level qjson.c once we have a native JSON output visitor; we

David objected to that in review of v3.  We agreed to disagree, and let
migration keep its own (incomplete) JSON visitor.  That's why I moved it
to migration/.

> could have renamed that one for less overall churn.  On the other
> hand, all of the QObject subtypes have their own qFOO.c file, but
> qjson.c makes it sound like we have a QTYPE_JSON subclass of
> QObject; the new name of qobject-json makes it obvious that the
> file is used for conversions between QObject and JSON, and not a
> QObject subtype.
>
> Kill trailing whitespace in the renamed tests/check-qobject-json.c
> to keep checkpatch.pl happy.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

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

* Re: [Qemu-devel] [PATCH v4 01/28] qapi: Rename (one) qjson.h to qobject-json.h
@ 2016-06-01 15:09     ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 15:09 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, Alexander Graf,
	open list:Block layer core, Alberto Garcia, Michael Roth,
	Chrysostomos Nanakos, Michael S. Tsirkin, Jeff Cody, Max Reitz,
	Christian Borntraeger, Gerd Hoffmann, open list:Overall,
	Cornelia Huck, Paolo Bonzini, Luiz Capitulino, Jason Wang,
	Richard Henderson

Eric Blake <eblake@redhat.com> writes:

> We have two different JSON visitors in the tree; and having both
> named 'qjson.h' can cause include confusion.  Rename the qapi
> version.

Commit 17b74b9 moved the other one to migration/.  If you still want to
rename this one, you'll need to update the commit message.

> Why did I pick that one?  A later patch plans on deleting the
> top-level qjson.c once we have a native JSON output visitor; we

David objected to that in review of v3.  We agreed to disagree, and let
migration keep its own (incomplete) JSON visitor.  That's why I moved it
to migration/.

> could have renamed that one for less overall churn.  On the other
> hand, all of the QObject subtypes have their own qFOO.c file, but
> qjson.c makes it sound like we have a QTYPE_JSON subclass of
> QObject; the new name of qobject-json makes it obvious that the
> file is used for conversions between QObject and JSON, and not a
> QObject subtype.
>
> Kill trailing whitespace in the renamed tests/check-qobject-json.c
> to keep checkpatch.pl happy.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

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

* Re: [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON Eric Blake
@ 2016-06-01 15:25   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 15:25 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Kevin Wolf, open list:Block layer core, Max Reitz

Eric Blake <eblake@redhat.com> writes:

> If our JSON output ever encounters an error, we would just silently
> leak the local variable.  Instead, assert that our usage won't fail.

Suggest "leak the error object".  Can do on commit.

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

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

* Re: [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_*
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_* Eric Blake
@ 2016-06-01 15:36   ` Markus Armbruster
  2016-06-07 23:20     ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 15:36 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core,
	Michael S. Tsirkin, Michael Roth, Alexander Graf,
	open list:sPAPR, Max Reitz, Andreas Färber, David Gibson

Eric Blake <eblake@redhat.com> writes:

> Rather than making the dealloc visitor track of stack of pointers
> remembered during visit_start_* in order to free them during
> visit_end_*, it's a lot easier to just make all callers pass the
> same pointer to visit_end_*.  The generated code has access to the
> same pointer, while all other users are doing virtual walks and
> can pass NULL.  The dealloc visitor is then greatly simplified.
>
> All three visit_end_*() functions intentionally take a void**,
> even though the visit_start_*() functions differ between void**,
> GenericList**, and GenericAlternate**.  This is done for several
> reasons: when doing a virtual walk, passing NULL doesn't care
> what the type is, but when doing a generated walk, we already
> have to cast the caller's specific FOO* to call visit_start,
> while using void** lets us use visit_end without a cast. Also,
> an upcoming patch will add a clone visitor that wants to use
> the same implementation for all three visit_end callbacks,
> which is made easier if all three share the same signature.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index aea90a1..84f32fc 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -145,7 +145,7 @@ static void qmp_input_check_struct(Visitor *v, Error **errp)
>      }
>  }
>
> -static void qmp_input_pop(Visitor *v)
> +static void qmp_input_pop(Visitor *v, void **obj)
>  {
>      QmpInputVisitor *qiv = to_qiv(v);
>      StackObject *tos = &qiv->stack[qiv->nb_stack - 1];

You could assert @obj matches tos->obj.  Same for the other visitors
that still need a stack.  Adding a stack to the ones that don't just for
the assertion seems excessive, though.

[...]

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

* Re: [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function Eric Blake
@ 2016-06-01 16:03   ` Markus Armbruster
  2016-06-03 11:46     ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 16:03 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Making each visitor provide its own (awkwardly-named) FOO_cleanup()
> is unusual, when we can instead have a polymorphic visit_free()
> interface.
>
> The dealloc visitor is the first one converted to completely use
> the new entry point, since only generated code and the testsuite
> were using it.  Diffs to the generated code look like:
>
> | void qapi_free_ACPIOSTInfo(ACPIOSTInfo *obj)
> | {
> |-    QapiDeallocVisitor *qdv;
> |     Visitor *v;
> |
> |     if (!obj) {
> |         return;
> |     }
> |
> |-    qdv = qapi_dealloc_visitor_new();
> |-    v = qapi_dealloc_get_visitor(qdv);
> |+    v = qapi_dealloc_visitor_new();

At this point, I wonder what this change has to do with the new
visit_free().  It turns out that...

> |     visit_type_ACPIOSTInfo(v, NULL, &obj, NULL);
> |-    qapi_dealloc_visitor_cleanup(qdv);

... this call is the only use of qdv, so you're eliminating it.

Next, I wonder whether this is worthwhile, as it creates an
inconsistency with the other visitors.  Peeking ahead, I see the
inconsistency is temporary: you're eliminating the other _get_visitor()
later in this series.  Okay, but perhaps you can explain your intentions
further up in the commit message.

> |+    visit_free(v);
> |}
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index dcfbf92..28d2203 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -513,6 +513,14 @@ opts_optional(Visitor *v, const char *name, bool *present)
>  }
>
>
> +static void
> +opts_free(Visitor *v)
> +{
> +    OptsVisitor *ov = to_ov(v);
> +    opts_visitor_cleanup(ov);

I'd eliminate the variable:

       opts_visitor_cleanup(to_ov(v));

Hmm, you put it to use when you inline opts_visitor_cleanup() in the
next patch.  Doesn't matter then.

> +}
> +
> +
>  OptsVisitor *
>  opts_visitor_new(const QemuOpts *opts)
>  {
> @@ -540,6 +548,7 @@ opts_visitor_new(const QemuOpts *opts)
>       * skip some mandatory methods... */
>
>      ov->visitor.optional = &opts_optional;
> +    ov->visitor.free = opts_free;
>
>      ov->opts_root = opts;
>
> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
> index 9391dea..235e8a1 100644
> --- a/qapi/qapi-dealloc-visitor.c
> +++ b/qapi/qapi-dealloc-visitor.c
> @@ -107,17 +107,12 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
>  {
>  }
>
> -Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
> -{
> -    return &v->visitor;
> -}
> -
> -void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
> +static void qapi_dealloc_free(Visitor *v)
>  {
>      g_free(v);

Uh, shouldn't this be g_free(v, QapiDeallocVisitor, visitor)?  That way,
we don't assume that visitor is QapiDeallocVisitor's first member.

>  }
>
> -QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
> +Visitor *qapi_dealloc_visitor_new(void)
>  {
>      QapiDeallocVisitor *v;
>
> @@ -138,6 +133,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
>      v->visitor.type_number = qapi_dealloc_type_number;
>      v->visitor.type_any = qapi_dealloc_type_anything;
>      v->visitor.type_null = qapi_dealloc_type_null;
> +    v->visitor.free = qapi_dealloc_free;
>
> -    return v;
> +    return &v->visitor;
>  }
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 84f32fc..3ca192e 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -375,6 +375,12 @@ Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
>      return &v->visitor;
>  }
>
> +static void qmp_input_free(Visitor *v)
> +{
> +    QmpInputVisitor *qiv = to_qiv(v);
> +    qmp_input_visitor_cleanup(qiv);
> +}
> +

Like for opts_free(), the variable becomes useful when
qmp_input_visitor_cleanup() gets inlined in a later patch.

Same for the other free methods.

>  void qmp_input_visitor_cleanup(QmpInputVisitor *v)
>  {
>      qobject_decref(v->root);
> @@ -403,6 +409,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
>      v->visitor.type_any = qmp_input_type_any;
>      v->visitor.type_null = qmp_input_type_null;
>      v->visitor.optional = qmp_input_optional;
> +    v->visitor.free = qmp_input_free;
>      v->strict = strict;
>
>      v->root = obj;
[...]

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

* Re: [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor new visit_free() function
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor " Eric Blake
@ 2016-06-01 16:06   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 16:06 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, Michael Roth, open list:Block layer core,
	Michael S. Tsirkin, Jason Wang, Luiz Capitulino, Igor Mammedov,
	Max Reitz, Andreas Färber, Eduardo Habkost

Eric Blake <eblake@redhat.com> writes:

> Now that we have a polymorphic visit_free(), we no longer need
> opts_visitor_cleanup(); which in turn means we no longer need
> to return a subtype from opts_visitor_new() nor a public upcast
> function.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 28d2203..6e65317 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -517,11 +517,15 @@ static void
>  opts_free(Visitor *v)
>  {
>      OptsVisitor *ov = to_ov(v);
> -    opts_visitor_cleanup(ov);

Blank line between declaration and statements, please.

> +    if (ov->unprocessed_opts != NULL) {
> +        g_hash_table_destroy(ov->unprocessed_opts);
> +    }
> +    g_free(ov->fake_id_opt);
> +    g_free(ov);
>  }
>
>
> -OptsVisitor *
> +Visitor *
>  opts_visitor_new(const QemuOpts *opts)
>  {
>      OptsVisitor *ov;
[...]

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

* Re: [Qemu-devel] [PATCH v4 07/28] string-input-visitor: Favor new visit_free() function
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 07/28] string-input-visitor: " Eric Blake
@ 2016-06-01 16:13   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 16:13 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Andreas Färber, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Now that we have a polymorphic visit_free(), we no longer need
> string_input_visitor_cleanup(); which in turn means we no longer
> need to return a subtype from string_input_visitor_new() nor a
> public upcast function.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/qom/object.c b/qom/object.c
> index 1562f7e..00dd68c 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -1216,7 +1216,7 @@ int object_property_get_enum(Object *obj, const char *name,
>  {
>      Error *err = NULL;
>      StringOutputVisitor *sov;
> -    StringInputVisitor *siv;
> +    Visitor *v;
>      char *str;
>      int ret;
>      ObjectProperty *prop = object_property_find(obj, name, errp);
> @@ -1243,13 +1243,12 @@ int object_property_get_enum(Object *obj, const char *name,
>          return 0;
>      }
>      str = string_output_get_string(sov);
> -    siv = string_input_visitor_new(str);
>      string_output_visitor_cleanup(sov);
> -    visit_type_enum(string_input_get_visitor(siv), name, &ret,
> -                    enumprop->strings, errp);
> +    v = string_input_visitor_new(str);
> +    visit_type_enum(v, name, &ret, enumprop->strings, errp);
>
>      g_free(str);
> -    string_input_visitor_cleanup(siv);
> +    visit_free(v);
>
>      return ret;
>  }
> @@ -1259,7 +1258,7 @@ void object_property_get_uint16List(Object *obj, const char *name,
>  {
>      Error *err = NULL;
>      StringOutputVisitor *ov;
> -    StringInputVisitor *iv;
> +    Visitor *v;
>      char *str;
>
>      ov = string_output_visitor_new(false);
> @@ -1270,11 +1269,11 @@ void object_property_get_uint16List(Object *obj, const char *name,
>          goto out;
>      }
>      str = string_output_get_string(ov);
> -    iv = string_input_visitor_new(str);
> -    visit_type_uint16List(string_input_get_visitor(iv), NULL, list, errp);
> +    v = string_input_visitor_new(str);
> +    visit_type_uint16List(v, NULL, list, errp);
>
>      g_free(str);
> -    string_input_visitor_cleanup(iv);
> +    visit_free(v);
>  out:
>      string_output_visitor_cleanup(ov);
>  }
> @@ -1282,11 +1281,9 @@ out:
>  void object_property_parse(Object *obj, const char *string,
>                             const char *name, Error **errp)
>  {
> -    StringInputVisitor *siv;
> -    siv = string_input_visitor_new(string);
> -    object_property_set(obj, string_input_get_visitor(siv), name, errp);
> -
> -    string_input_visitor_cleanup(siv);
> +    Visitor *v = string_input_visitor_new(string);

Blank line between declaration and statements, please.

> +    object_property_set(obj, v, name, errp);
> +    visit_free(v);
>  }
>
>  char *object_property_print(Object *obj, const char *name, bool human,
[...]

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

* Re: [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: Favor new visit_free() function
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: " Eric Blake
@ 2016-06-01 16:19   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 16:19 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Michael Roth, Gerd Hoffmann, Paolo Bonzini,
	Andreas Färber

Eric Blake <eblake@redhat.com> writes:

> Now that we have a polymorphic visit_free(), we no longer need
> qmp_input_visitor_cleanup(); which in turn means we no longer
> need to return a subtype from qmp_input_visitor_new() nor a
> public upcast function.
>
> Generated code changes to qmp-marshal look like:

qmp-marshal.c

>
> |@@ -52,11 +52,10 @@ void qmp_marshal_add_fd(QDict *args, QOb
> | {
> |     Error *err = NULL;
> |     AddfdInfo *retval;
> |-    QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true);
> |     Visitor *v;
> |     q_obj_add_fd_arg arg = {0};
> |
> |-    v = qmp_input_get_visitor(qiv);
> |+    v = qmp_input_visitor_new(QOBJECT(args), true);
> |     visit_start_struct(v, NULL, NULL, 0, &err);
> |     if (err) {
> |         goto out;
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/tests/check-qnull.c b/tests/check-qnull.c
> index fd9c68f..c8021dc 100644
> --- a/tests/check-qnull.c
> +++ b/tests/check-qnull.c
> @@ -39,7 +39,7 @@ static void qnull_visit_test(void)
>  {
>      QObject *obj;
>      QmpOutputVisitor *qov;
> -    QmpInputVisitor *qiv;
> +    Visitor *qiv;

Call it v instead of qiv?  You do that elsewhere...  Your choice.

>
>      /*
>       * Most tests of interactions between QObject and visitors are in
> @@ -51,8 +51,8 @@ static void qnull_visit_test(void)
>      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);
> +    visit_type_null(qiv, NULL, &error_abort);
> +    visit_free(qiv);
>
>      qov = qmp_output_visitor_new();
>      visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort);
[...]

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

* Re: [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests Eric Blake
@ 2016-06-01 16:33   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 16:33 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Create a new visitor_get() function to capture common
> actions taken in collecting output from an output visitor,
> to make it easier to refactor the output visitors in a
> later patch. While making test improvements, also use
> &error_abort and error_free_or_abort() in more places, use
> the generated qapi_free_intList() instead of open-coding it,
> reduce the scope of some variables, and copy the visitor_reset()
> concept from the qmp-output test to the string-output test.

Sounds like this would be easier to review if split into parts.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: new patch
> ---
>  tests/test-qmp-output-visitor.c    | 78 +++++++++++-------------------
>  tests/test-string-output-visitor.c | 97 ++++++++++++++++++--------------------
>  2 files changed, 72 insertions(+), 103 deletions(-)
>
> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index d638ea6..0362192 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -24,6 +24,7 @@
>  typedef struct TestOutputVisitorData {
>      QmpOutputVisitor *qov;
>      Visitor *ov;
> +    QObject *obj;
>  } TestOutputVisitorData;
>
>  static void visitor_output_setup(TestOutputVisitorData *data,
> @@ -42,6 +43,15 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
>      visit_free(data->ov);
>      data->qov = NULL;
>      data->ov = NULL;
> +    qobject_decref(data->obj);
> +    data->obj = NULL;
> +}
> +
> +static QObject *visitor_get(TestOutputVisitorData *data)
> +{
> +    data->obj = qmp_output_get_qobject(data->qov);
> +    g_assert(data->obj);
> +    return data->obj;
>  }
>
>  static void visitor_reset(TestOutputVisitorData *data)
> @@ -58,12 +68,9 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
>
>      visit_type_int(data->ov, NULL, &value, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QINT);
>      g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value);
> -
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_bool(TestOutputVisitorData *data,
> @@ -74,12 +81,9 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
>
>      visit_type_bool(data->ov, NULL, &value, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QBOOL);
>      g_assert(qbool_get_bool(qobject_to_qbool(obj)) == value);
> -
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_number(TestOutputVisitorData *data,
> @@ -90,12 +94,9 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
>
>      visit_type_number(data->ov, NULL, &value, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QFLOAT);
>      g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
> -
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_string(TestOutputVisitorData *data,
> @@ -106,12 +107,9 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
>
>      visit_type_str(data->ov, NULL, &string, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QSTRING);
>      g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
> -
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_no_string(TestOutputVisitorData *data,
> @@ -123,12 +121,9 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
>      /* A null string should return "" */
>      visit_type_str(data->ov, NULL, &string, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QSTRING);
>      g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, "");
> -
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_enum(TestOutputVisitorData *data,
> @@ -140,12 +135,10 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
>      for (i = 0; i < ENUM_ONE__MAX; i++) {
>          visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
>
> -        obj = qmp_output_get_qobject(data->qov);
> -        g_assert(obj != NULL);
> +        obj = visitor_get(data);
>          g_assert(qobject_type(obj) == QTYPE_QSTRING);
>          g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
>                          EnumOne_lookup[i]);
> -        qobject_decref(obj);
>          visitor_reset(data);
>      }
>  }
> @@ -178,8 +171,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
>
>      visit_type_TestStruct(data->ov, NULL, &p, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QDICT);
>
>      qdict = qobject_to_qdict(obj);
> @@ -187,8 +179,6 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
>      g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
>      g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false);
>      g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
> -
> -    QDECREF(qdict);
>  }
>
>  static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
> @@ -223,8 +213,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
>
>      visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QDICT);
>
>      qdict = qobject_to_qdict(obj);
> @@ -251,7 +240,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
>      g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
>      g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
>
> -    QDECREF(qdict);
>      qapi_free_UserDefTwo(ud2);
>  }
>
> @@ -303,8 +291,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
>
>      visit_type_TestStructList(data->ov, NULL, &head, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QLIST);
>
>      qlist = qobject_to_qlist(obj);
> @@ -325,7 +312,6 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
>      }
>      g_assert_cmpint(i, ==, max_items);
>
> -    QDECREF(qlist);
>      qapi_free_TestStructList(head);
>  }
>
> @@ -369,11 +355,9 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
>
>      qobj = QOBJECT(qint_from_int(-42));
>      visit_type_any(data->ov, NULL, &qobj, &error_abort);
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);
> +    obj = visitor_get(data);
>      g_assert(qobject_type(obj) == QTYPE_QINT);
>      g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42);
> -    qobject_decref(obj);
>      qobject_decref(qobj);
>
>      visitor_reset(data);
> @@ -383,9 +367,8 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
>      qdict_put(qdict, "string", qstring_from_str("foo"));
>      qobj = QOBJECT(qdict);
>      visit_type_any(data->ov, NULL, &qobj, &error_abort);
> +    obj = visitor_get(data);

Any particular reason to put this here...

>      qobject_decref(qobj);
> -    obj = qmp_output_get_qobject(data->qov);
> -    g_assert(obj != NULL);

... rather than here?

>      qdict = qobject_to_qdict(obj);
>      g_assert(qdict);
>      qobj = qdict_get(qdict, "integer");
> @@ -403,7 +386,6 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
>      qstring = qobject_to_qstring(qobj);
>      g_assert(qstring);
>      g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_union_flat(TestOutputVisitorData *data,
> @@ -419,7 +401,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
>      tmp->u.value1.boolean = true;
>
>      visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
> -    arg = qmp_output_get_qobject(data->qov);
> +    arg = visitor_get(data);
>
>      g_assert(qobject_type(arg) == QTYPE_QDICT);
>      qdict = qobject_to_qdict(arg);
> @@ -430,7 +412,6 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
>      g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
>
>      qapi_free_UserDefFlatUnion(tmp);
> -    QDECREF(qdict);
>  }
>
>  static void test_visitor_out_alternate(TestOutputVisitorData *data,
> @@ -445,13 +426,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
>      tmp->u.i = 42;
>
>      visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
> -    arg = qmp_output_get_qobject(data->qov);
> +    arg = visitor_get(data);
>
>      g_assert(qobject_type(arg) == QTYPE_QINT);
>      g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42);
>
>      qapi_free_UserDefAlternate(tmp);
> -    qobject_decref(arg);
>
>      visitor_reset(data);
>      tmp = g_new0(UserDefAlternate, 1);
> @@ -459,13 +439,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
>      tmp->u.s = g_strdup("hello");
>
>      visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
> -    arg = qmp_output_get_qobject(data->qov);
> +    arg = visitor_get(data);
>
>      g_assert(qobject_type(arg) == QTYPE_QSTRING);
>      g_assert_cmpstr(qstring_get_str(qobject_to_qstring(arg)), ==, "hello");
>
>      qapi_free_UserDefAlternate(tmp);
> -    qobject_decref(arg);
>
>      visitor_reset(data);
>      tmp = g_new0(UserDefAlternate, 1);
> @@ -476,7 +455,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
>      tmp->u.udfu.u.value1.boolean = true;
>
>      visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
> -    arg = qmp_output_get_qobject(data->qov);
> +    arg = visitor_get(data);
>
>      g_assert_cmpint(qobject_type(arg), ==, QTYPE_QDICT);
>      qdict = qobject_to_qdict(arg);
> @@ -487,7 +466,6 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
>      g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
>
>      qapi_free_UserDefAlternate(tmp);
> -    qobject_decref(arg);
>  }
>
>  static void test_visitor_out_null(TestOutputVisitorData *data,
> @@ -501,14 +479,13 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
>      visit_type_null(data->ov, "a", &error_abort);
>      visit_check_struct(data->ov, &error_abort);
>      visit_end_struct(data->ov, NULL);
> -    arg = qmp_output_get_qobject(data->qov);
> +    arg = visitor_get(data);
>      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);
>  }
>
>  static void init_native_list(UserDefNativeListUnion *cvalue)
> @@ -739,10 +716,9 @@ static void test_native_list(TestOutputVisitorData *data,
>
>      visit_type_UserDefNativeListUnion(data->ov, NULL, &cvalue, &error_abort);
>
> -    obj = qmp_output_get_qobject(data->qov);
> +    obj = visitor_get(data);
>      check_native_list(obj, cvalue->type);
>      qapi_free_UserDefNativeListUnion(cvalue);
> -    qobject_decref(obj);
>  }
>
>  static void test_visitor_out_native_list_int(TestOutputVisitorData *data,
> diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
> index 2a62a2f..d5dd8c8 100644
> --- a/tests/test-string-output-visitor.c
> +++ b/tests/test-string-output-visitor.c
> @@ -23,29 +23,30 @@
>  typedef struct TestOutputVisitorData {
>      StringOutputVisitor *sov;
>      Visitor *ov;
> +    char *str;
>      bool human;
>  } TestOutputVisitorData;
>
> +static void visitor_output_setup_internal(TestOutputVisitorData *data,
> +                                          bool human)
> +{
> +    data->human = human;
> +    data->sov = string_output_visitor_new(human);
> +    g_assert(data->sov);
> +    data->ov = string_output_get_visitor(data->sov);
> +    g_assert(data->ov);
> +}
> +
>  static void visitor_output_setup(TestOutputVisitorData *data,
>                                   const void *unused)
>  {
> -    data->human = false;
> -    data->sov = string_output_visitor_new(data->human);
> -    g_assert(data->sov != NULL);
> -
> -    data->ov = string_output_get_visitor(data->sov);
> -    g_assert(data->ov != NULL);
> +    return visitor_output_setup_internal(data, false);
>  }
>
>  static void visitor_output_setup_human(TestOutputVisitorData *data,
>                                         const void *unused)
>  {
> -    data->human = true;
> -    data->sov = string_output_visitor_new(data->human);
> -    g_assert(data->sov != NULL);
> -
> -    data->ov = string_output_get_visitor(data->sov);
> -    g_assert(data->ov != NULL);
> +    return visitor_output_setup_internal(data, true);
>  }
>
>  static void visitor_output_teardown(TestOutputVisitorData *data,
> @@ -54,6 +55,23 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
>      visit_free(data->ov);
>      data->sov = NULL;
>      data->ov = NULL;
> +    g_free(data->str);
> +    data->str = NULL;
> +}
> +
> +static char *visitor_get(TestOutputVisitorData *data)
> +{
> +    data->str = string_output_get_string(data->sov);
> +    g_assert(data->str);
> +    return data->str;
> +}
> +
> +static void visitor_reset(TestOutputVisitorData *data)
> +{
> +    bool human = data->human;
> +
> +    visitor_output_teardown(data, NULL);
> +    visitor_output_setup_internal(data, human);
>  }
>
>  static void test_visitor_out_int(TestOutputVisitorData *data,
> @@ -66,14 +84,12 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
>      visit_type_int(data->ov, NULL, &value, &err);
>      g_assert(!err);
>
> -    str = string_output_get_string(data->sov);
> -    g_assert(str != NULL);
> +    str = visitor_get(data);
>      if (data->human) {
>          g_assert_cmpstr(str, ==, "42 (0x2a)");
>      } else {
>          g_assert_cmpstr(str, ==, "42");
>      }
> -    g_free(str);
>  }
>
>  static void test_visitor_out_intList(TestOutputVisitorData *data,
> @@ -95,8 +111,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
>      visit_type_intList(data->ov, NULL, &list, &err);
>      g_assert(err == NULL);
>
> -    str = string_output_get_string(data->sov);
> -    g_assert(str != NULL);
> +    str = visitor_get(data);
>      if (data->human) {
>          g_assert_cmpstr(str, ==,
>              "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 "
> @@ -106,13 +121,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data,
>          g_assert_cmpstr(str, ==,
>              "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807");
>      }
> -    g_free(str);
> -    while (list) {
> -        intList *tmp2;
> -        tmp2 = list->next;
> -        g_free(list);
> -        list = tmp2;
> -    }
> +    qapi_free_intList(list);
>  }
>
>  static void test_visitor_out_bool(TestOutputVisitorData *data,
> @@ -125,10 +134,8 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
>      visit_type_bool(data->ov, NULL, &value, &err);
>      g_assert(!err);
>
> -    str = string_output_get_string(data->sov);
> -    g_assert(str != NULL);
> +    str = visitor_get(data);
>      g_assert_cmpstr(str, ==, "true");
> -    g_free(str);
>  }
>
>  static void test_visitor_out_number(TestOutputVisitorData *data,
> @@ -141,10 +148,8 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
>      visit_type_number(data->ov, NULL, &value, &err);
>      g_assert(!err);
>
> -    str = string_output_get_string(data->sov);
> -    g_assert(str != NULL);
> +    str = visitor_get(data);
>      g_assert_cmpstr(str, ==, "3.140000");
> -    g_free(str);
>  }
>
>  static void test_visitor_out_string(TestOutputVisitorData *data,
> @@ -158,61 +163,50 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
>      visit_type_str(data->ov, NULL, &string, &err);
>      g_assert(!err);
>
> -    str = string_output_get_string(data->sov);
> -    g_assert(str != NULL);
> +    str = visitor_get(data);
>      if (data->human) {
>          g_assert_cmpstr(str, ==, string_human);
>      } else {
>          g_assert_cmpstr(str, ==, string);
>      }
> -    g_free(str);
>  }
>
>  static void test_visitor_out_no_string(TestOutputVisitorData *data,
>                                         const void *unused)
>  {
>      char *string = NULL;
> -    Error *err = NULL;
>      char *str;
>
>      /* A null string should return "" */
> -    visit_type_str(data->ov, NULL, &string, &err);
> -    g_assert(!err);
> +    visit_type_str(data->ov, NULL, &string, &error_abort);
>
> -    str = string_output_get_string(data->sov);
> -    g_assert(str != NULL);
> +    str = visitor_get(data);
>      if (data->human) {
>          g_assert_cmpstr(str, ==, "<null>");
>      } else {
>          g_assert_cmpstr(str, ==, "");
>      }
> -    g_free(str);
>  }
>
>  static void test_visitor_out_enum(TestOutputVisitorData *data,
>                                    const void *unused)
>  {
> -    Error *err = NULL;
>      char *str;
>      EnumOne i;
>
>      for (i = 0; i < ENUM_ONE__MAX; i++) {
> -        char *str_human;
> +        visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
>
> -        visit_type_EnumOne(data->ov, "unused", &i, &err);
> -        g_assert(!err);
> -
> -        str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
> -
> -        str = string_output_get_string(data->sov);
> -        g_assert(str != NULL);
> +        str = visitor_get(data);
>          if (data->human) {
> +            char *str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]);
> +
>              g_assert_cmpstr(str, ==, str_human);
> +            g_free(str_human);
>          } else {
>              g_assert_cmpstr(str, ==, EnumOne_lookup[i]);
>          }
> -        g_free(str_human);
> -	g_free(str);
> +        visitor_reset(data);
>      }
>  }
>
> @@ -225,8 +219,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
>      for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
>          err = NULL;
>          visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
> -        g_assert(err);
> -        error_free(err);
> +        error_free_or_abort(&err);
>      }
>  }

All right, this wasn't too bad.  I'd still split it up, but I'm a neat
freak in that regard.  Your choice.

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

* Re: [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function Eric Blake
@ 2016-06-01 17:02   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-01 17:02 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core, Jason Wang,
	Michael Roth, Luiz Capitulino, Gerd Hoffmann, Paolo Bonzini,
	Max Reitz, Andreas Färber

Eric Blake <eblake@redhat.com> writes:

> Making each output visitor provide its own output collection
> function was the only remaining reason for exposing visitor
> sub-types to the rest of the code base.  Add a polymorphic
> visit_complete() function which is a no-op for input visitors,
> and which populates an opaque pointer for output visitors.  For
> maximum type-safety, also add a parameter to the output visitor
> constructors with a type-correct version of the output pointer,
> and assert that the two uses match.
>
> This approach was considered superior to either passing the
> output parameter only during construction (action at a distance
> during visit_free() feels awkward) or only during visit_complete()
> (defeating type safety makes it easier to use incorrectly).

Reasonable.

> Most callers were function-local, and therefore a mechanical
> conversion; the testsuite was a bit trickier, but the previous
> cleanup patch minimized the churn here.
>
> Generated code is simplified as follows for events:
>
> |@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
> |     QDict *qmp;
> |     Error *err = NULL;
> |     QMPEventFuncEmit emit;
> |-    QmpOutputVisitor *qov;
> |+    QObject *obj;
> |     Visitor *v;
> |     q_obj_ACPI_DEVICE_OST_arg param = {
> |         info
> |@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
> |
> |     qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
> |
> |-    qov = qmp_output_visitor_new();
> |-    v = qmp_output_get_visitor(qov);
> |+    v = qmp_output_visitor_new(&obj);
> |
> |     visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
> |     if (err) {
> |@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
> |         goto out;
> |     }
> |
> |-    qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
> |+    visit_complete(v, &obj);
> |+    qdict_put_obj(qmp, "data", obj);
> |     emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
>
> and for commands:
>
> | {
> |     Error *err = NULL;
> |-    QmpOutputVisitor *qov = qmp_output_visitor_new();
> |     Visitor *v;
> |
> |-    v = qmp_output_get_visitor(qov);
> |+    v = qmp_output_visitor_new(ret_out);
> |     visit_type_AddfdInfo(v, "unused", &ret_in, &err);
> |-    if (err) {
> |-        goto out;
> |+    if (!err) {
> |+        visit_complete(v, ret_out);
> |     }
> |-    *ret_out = qmp_output_get_qobject(qov);
> |-
> |-out:
> |     error_propagate(errp, err);
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: new patch, inspired by review of v3 7/18
> ---
>  include/qapi/visitor.h               | 52 +++++++++++++++++++++---------------
>  include/qapi/visitor-impl.h          |  3 +++
>  scripts/qapi-commands.py             | 10 +++----
>  scripts/qapi-event.py                |  8 +++---
>  include/qapi/qmp-output-visitor.h    | 11 +++++---
>  include/qapi/string-output-visitor.h | 10 ++++---
>  qapi/qapi-visit-core.c               | 10 +++++++
>  block/qapi.c                         |  9 +++----
>  blockdev.c                           |  9 +++----
>  hmp.c                                | 11 ++++----
>  net/net.c                            | 11 ++++----
>  qapi/qmp-output-visitor.c            | 21 ++++++++-------
>  qapi/string-output-visitor.c         | 22 ++++++++-------
>  qemu-img.c                           | 30 ++++++++++-----------
>  qom/object.c                         | 28 +++++++++----------
>  qom/qom-qobject.c                    | 10 +++----
>  replay/replay-input.c                |  6 ++---
>  tests/check-qnull.c                  | 17 ++++++------
>  tests/test-qmp-output-visitor.c      | 11 +++-----
>  tests/test-string-output-visitor.c   |  8 ++----
>  tests/test-visitor-serialization.c   | 22 ++++++++-------
>  util/qemu-sockets.c                  |  6 ++---
>  22 files changed, 167 insertions(+), 158 deletions(-)
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 2ded852..b3bd97c 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -39,21 +39,15 @@
>   *
>   * All of the visitors are created via:
>   *
> - * Type *subtype_visitor_new(parameters...);
> - *
> - * where Type is either directly 'Visitor *', or is a subtype that can
> - * be trivially upcast to Visitor * via another function:
> - *
> - * Visitor *subtype_get_visitor(SubtypeVisitor *);
> + * Visitor *subtype_visitor_new(parameters...);
>   *
>   * A visitor should be used for exactly one top-level visit_type_FOO()
> - * or virtual walk, then passed to visit_free() to clean up resources.
> - * It is okay to free the visitor without completing the visit, if
> - * some other error is detected in the meantime.  Output visitors
> - * provide an additional function, for collecting the final results of
> - * a successful visit: string_output_get_string() and
> - * qmp_output_get_qobject(); this collection function should not be
> - * called if any errors were reported during the visit.
> + * or virtual walk; if that is successful, the caller can optionally
> + * call visit_complete() (most useful for output visits).  Then,

Well, it's mostly useless for any other visits, isn't it?

> + * regardless of success or failure, the user should call visit_free()
> + * to clean up resources.  It is okay to free the visitor without
> + * completing the visit, if some other error is detected in the
> + * meantime.
>   *
>   * All QAPI types have a corresponding function with a signature
>   * roughly compatible with this:
> @@ -123,14 +117,14 @@
>   *  Error *err = NULL;
>   *  Visitor *v;
>   *
> - *  v = ...obtain input visitor...
> + *  v = FOO_visitor_new(...);
>   *  visit_type_Foo(v, NULL, &f, &err);
>   *  if (err) {
>   *      ...handle error...
>   *  } else {
>   *      ...use f...
>   *  }
> - *  ...clean up v...
> + *  visit_free(v);
>   *  qapi_free_Foo(f);
>   * </example>
>   *
> @@ -140,7 +134,7 @@
>   *  Error *err = NULL;
>   *  Visitor *v;
>   *
> - *  v = ...obtain input visitor...
> + *  v = FOO_visitor_new(...);
>   *  visit_type_FooList(v, NULL, &l, &err);
>   *  if (err) {
>   *      ...handle error...
> @@ -149,7 +143,7 @@
>   *          ...use l->value...
>   *      }
>   *  }
> - *  ...clean up v...
> + *  visit_free(v);
>   *  qapi_free_FooList(l);
>   * </example>
>   *
> @@ -159,13 +153,17 @@
>   *  Foo *f = ...obtain populated object...
>   *  Error *err = NULL;
>   *  Visitor *v;
> + *  Type *result;
>   *
> - *  v = ...obtain output visitor...
> + *  v = FOO_visitor_new(..., &result);
>   *  visit_type_Foo(v, NULL, &f, &err);
>   *  if (err) {
>   *      ...handle error...
> + *  } else {
> + *      visit_complete(v, &result);
> + *      ...use result...
>   *  }
> - *  ...clean up v...
> + *  visit_free(v);
>   * </example>
>   *
>   * When visiting a real QAPI struct, this file provides several
> @@ -191,7 +189,7 @@
>   *  Error *err = NULL;
>   *  int value;
>   *
> - *  v = ...obtain visitor...
> + *  v = FOO_visitor_new(...);
>   *  visit_start_struct(v, NULL, NULL, 0, &err);
>   *  if (err) {
>   *      goto out;
> @@ -219,7 +217,7 @@
>   *  visit_end_struct(v, NULL);
>   * out:
>   *  error_propagate(errp, err);
> - *  ...clean up v...
> + *  visit_free(v);
>   * </example>
>   */
>
> @@ -243,6 +241,18 @@ typedef struct GenericAlternate {
>  /*** Visitor cleanup ***/
>
>  /*
> + * Complete the visit, collecting any output.
> + *
> + * May only be called after a successful top-level visit_type_FOO() or
> + * visit_end_ITEM(), and marks the end of the visit.  The @opaque
> + * pointer should match the output parameter passed to the
> + * subtype_visitor_new() used to create an output visitor, or NULL for
> + * any other visitor.  Needed for output visitors, but may also be
> + * called with other visitors.
> + */
> +void visit_complete(Visitor *v, void *opaque);
> +
> +/*
>   * Free @v and any resources it has tied up.
>   *
>   * May be called whether or not the visit has been successfully
[...]
> diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h
> index 29c9a2e..040fdda 100644
> --- a/include/qapi/qmp-output-visitor.h
> +++ b/include/qapi/qmp-output-visitor.h
> @@ -19,9 +19,12 @@
>
>  typedef struct QmpOutputVisitor QmpOutputVisitor;
>
> -QmpOutputVisitor *qmp_output_visitor_new(void);
> -
> -QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
> -Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
> +/*
> + * Create a new QMP output visitor.
> + *
> + * If everything else succeeds, pass @result to visit_complete() to
> + * collect the result of the visit.
> + */
> +Visitor *qmp_output_visitor_new(QObject **result);
>
>  #endif

One qmp_output_get_visitor() left behind in docs/qapi-code-gen.txt.

> diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h
> index 03e377e..a31054e 100644
> --- a/include/qapi/string-output-visitor.h
> +++ b/include/qapi/string-output-visitor.h
> @@ -18,13 +18,15 @@
>  typedef struct StringOutputVisitor StringOutputVisitor;
>
>  /*
> + * Create a new string output visitor.
> + *
> + * If everything else succeeds, pass @result to visit_complete() to
> + * collect the result of the visit.
> + *

Document @human while we're here?

>   * The string output visitor does not implement support for visiting
>   * 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);
> -
> -char *string_output_get_string(StringOutputVisitor *v);
> -Visitor *string_output_get_visitor(StringOutputVisitor *v);
> +Visitor *string_output_visitor_new(bool human, char **result);
>
>  #endif
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 5f68c25..279ea8e 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -20,6 +20,16 @@
>  #include "qapi/visitor.h"
>  #include "qapi/visitor-impl.h"
>
> +void visit_complete(Visitor *v, void *opaque)
> +{
> +    if (v->type == VISITOR_OUTPUT) {
> +        assert(v->complete);
> +    }

       assert(v->type != VISITOR_OUTPUT || v->complete);

> +    if (v->complete) {
> +        v->complete(v, opaque);
> +    }
> +}
> +
>  void visit_free(Visitor *v)
>  {
>      if (v) {
[...]
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 80bc682..2dac302 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -32,6 +32,7 @@ struct QmpOutputVisitor
>      Visitor visitor;
>      QStack stack; /* Stack of containers that haven't yet been finished */
>      QObject *root; /* Root of the output visit */
> +    QObject **result; /* User's storage location for result */
>  };
>
>  #define qmp_output_add(qov, name, value) \
> @@ -195,18 +196,17 @@ static void qmp_output_type_null(Visitor *v, const char *name, Error **errp)
>  /* 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)
> +static void qmp_output_complete(Visitor *v, void *opaque)
>  {
> +    QmpOutputVisitor *qov = to_qov(v);
> +
>      /* A visit must have occurred, with each start paired with end.  */
>      assert(qov->root && QTAILQ_EMPTY(&qov->stack));
> +    assert(opaque == qov->result);
>
>      qobject_incref(qov->root);
> -    return qov->root;
> -}
> -
> -Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
> -{
> -    return &v->visitor;
> +    *qov->result = qov->root;
> +    qov->result = NULL;

Why zap qov->result?

If we want to do that, we better spell out that visit_complete() may be
called at most once.

I like idempotent functions... but see string_output_complete() below.

>  }
>
>  static void qmp_output_free(Visitor *v)
> @@ -223,7 +223,7 @@ static void qmp_output_free(Visitor *v)
>      g_free(qov);
>  }
>
> -QmpOutputVisitor *qmp_output_visitor_new(void)
> +Visitor *qmp_output_visitor_new(QObject **result)
>  {
>      QmpOutputVisitor *v;
>
> @@ -242,9 +242,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
>      v->visitor.type_number = qmp_output_type_number;
>      v->visitor.type_any = qmp_output_type_any;
>      v->visitor.type_null = qmp_output_type_null;
> +    v->visitor.complete = qmp_output_complete;
>      v->visitor.free = qmp_output_free;
>
>      QTAILQ_INIT(&v->stack);
> +    *result = NULL;
> +    v->result = result;
>
> -    return v;
> +    return &v->visitor;
>  }
> diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
> index 91ee694..c1c5942 100644
> --- a/qapi/string-output-visitor.c
> +++ b/qapi/string-output-visitor.c
> @@ -58,6 +58,7 @@ struct StringOutputVisitor
>      Visitor visitor;
>      bool human;
>      GString *string;
> +    char **result;
>      ListMode list_mode;
>      union {
>          int64_t s;
> @@ -302,16 +303,13 @@ static void end_list(Visitor *v, void **obj)
>      sov->list_mode = LM_NONE;
>  }
>
> -char *string_output_get_string(StringOutputVisitor *sov)
> +static void string_output_complete(Visitor *v, void *opaque)
>  {
> -    char *string = g_string_free(sov->string, false);
> +    StringOutputVisitor *sov = to_sov(v);
> +
> +    assert(opaque == sov->result);
> +    *sov->result = g_string_free(sov->string, false);
>      sov->string = NULL;

Why zap sov->string?

I guess it lets you transfer ownership of the string inside the GString
without copying it.  An idempotent string_output_complete() would have
to copy.  Okay, I can buy that argument for making it non-idempotent.

> -    return string;
> -}
> -
> -Visitor *string_output_get_visitor(StringOutputVisitor *sov)
> -{
> -    return &sov->visitor;
>  }
>
>  static void free_range(void *range, void *dummy)
> @@ -332,7 +330,7 @@ static void string_output_free(Visitor *v)
>      g_free(sov);
>  }
>
> -StringOutputVisitor *string_output_visitor_new(bool human)
> +Visitor *string_output_visitor_new(bool human, char **result)
>  {
>      StringOutputVisitor *v;
>
> @@ -340,6 +338,9 @@ StringOutputVisitor *string_output_visitor_new(bool human)
>
>      v->string = g_string_new(NULL);
>      v->human = human;
> +    v->result = result;
> +    *result = NULL;
> +
>      v->visitor.type = VISITOR_OUTPUT;
>      v->visitor.type_int64 = print_type_int64;
>      v->visitor.type_uint64 = print_type_uint64;
> @@ -350,7 +351,8 @@ StringOutputVisitor *string_output_visitor_new(bool human)
>      v->visitor.start_list = start_list;
>      v->visitor.next_list = next_list;
>      v->visitor.end_list = end_list;
> +    v->visitor.complete = string_output_complete;
>      v->visitor.free = string_output_free;
>
> -    return v;
> +    return &v->visitor;
>  }
[...]

I rather like how PATCH 05-12 reduce boilerplate in visitor usage.

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

* Re: [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor
  2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor Eric Blake
@ 2016-06-02 13:43   ` Markus Armbruster
  2016-06-03 14:04     ` Markus Armbruster
  2016-06-09  4:15     ` Eric Blake
  0 siblings, 2 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-02 13:43 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> We have a couple places in the code base that want to deep-clone
> one QAPI object into another, and they were resorting to serializing
> the struct out to QObject then reparsing it.  A much more efficient
> version can be done by adding a new clone visitor.
>
> Since cloning is still relatively uncommon, expose the use of the
> new visitor via a QAPI_CLONE() macro that takes care of type-punning
> the underlying function pointer, rather than generating lots of
> unused functions for types that won't be cloned.  And yes, we're
> relying on the compiler treating all pointers equally, even though
> a strict C program cannot portably do so - but we're not the first
> one in the qemu code base to expect it to work (hello, glib!).
>
> Note that we can only clone objects (including alternates) and lists,
> not built-ins or enums.  The visitor core hides integer width from
> the actual visitor (since commit 04e070d), and as long as that's the
> case, we can't clone top-level integers.  Then again, those can
> always be cloned by direct copy, since they are not objects with
> deep pointers, so it's no real loss.  And restricting cloning to
> just objects and lists is cleaner than restricting it to non-integers.
> As such, I documented that the clone visitor is for direct use only
> by code internal to QAPI.
>
> Scalars not at the root of the clone copy just fine, by virtue of a
> g_memdup() each time we push another struct onto the stack.
>
> Cloning an 'any' type could be possible by incrementing the QObject
> refcnt, but it's not obvious whether that is better than implementing
> a QObject deep clone.  So for now, we document it as unsupported,
> and intentionally omit the .type_any() callback to let a developer
> know their usage needs implementation.
>
> The choice of adding a fourth visitor type deserves some explanation.
> On the surface, the clone visitor is mostly an input visitor (it
> takes arbitrary input - in this case, another QAPI object - and
> creates a new QAPI object during the course of the visit).  But
> ever since commit da72ab0 consolidated enum visits based on the
> visitor type, using VISITOR_INPUT would cause us to run
> visit_type_str(), even though for cloning there is nothing to do
> (we just copy the enum value across, without regards to its mapping
> to strings).   Also, since our input happens to be a QAPI object,
> we can also satisfy the internal checks for VISITOR_OUTPUT.  So in
> the end, I settled with a new VISITOR_CLONE, and chose its value
> such that many internal checks can use 'v->type & mask', sticking
> to 'v->type == value' where the difference matters.
>
> Add testsuite coverage for several different clone situations, to
> ensure that the code is working.  I also tested that valgrind was
> happy with the test.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: hoist earlier in series, drop wasted generated functions and
> replace it with QAPI_CLONE() macro, drop 'any' support, tweak
> core assertion checking, internal improve commit message
> v3: new patch
> ---
>  include/qapi/visitor.h       |  33 ++++---
>  include/qapi/visitor-impl.h  |  12 +--
>  include/qapi/clone-visitor.h |  37 ++++++++
>  qapi/qapi-visit-core.c       |  18 ++--
>  qapi/qapi-clone-visitor.c    | 178 +++++++++++++++++++++++++++++++++++++
>  tests/test-clone-visitor.c   | 207 +++++++++++++++++++++++++++++++++++++++++++
>  qapi/Makefile.objs           |   2 +-
>  tests/.gitignore             |   1 +
>  tests/Makefile               |   4 +
>  9 files changed, 466 insertions(+), 26 deletions(-)
>  create mode 100644 include/qapi/clone-visitor.h
>  create mode 100644 qapi/qapi-clone-visitor.c
>  create mode 100644 tests/test-clone-visitor.c
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index b3bd97c..3f46921 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -24,15 +24,16 @@
>   * 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,
> + * There are four 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
> + * a completed QAPI graph and generate an external representation, the
> + * dealloc visitor can take a QAPI graph (possibly partially
> + * constructed) and recursively free its resources, and the clone
> + * visitor performs a deep clone of one QAPI object to another.  While
> + * the dealloc and QMP input/output visitors are general, the string,
> + * QemuOpts, and clone 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.
[...]
    * 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.

What about the clone visitor?

> @@ -102,11 +103,19 @@
>   *
>   * 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:
> + * where behaves like free() in that @obj may be NULL.  Such objects
> + * may also be used with the following macro, provided alongside the
> + * clone visitor:
> + *
> + * Type *QAPI_CLONE(Type, src);
> + *
> + * in order to perform a deep clone of @src.  Because of the generated
> + * qapi_free functions and the QAPI_CLONE() macro, the clone and
> + * dealloc visitor should not be used directly outside of QAPI 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);
>   *



> diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
> index 16e0b86..29fac2b 100644
> --- a/include/qapi/visitor-impl.h
> +++ b/include/qapi/visitor-impl.h
> @@ -27,14 +27,16 @@
>   */
>
>  /*
> - * There are three classes of visitors; setting the class determines
> + * There are four classes of visitors; setting the class determines
>   * how QAPI enums are visited, as well as what additional restrictions
> - * can be asserted.
> + * can be asserted.  The values are intentionally chosen so as to
> + * permit some assertions based on whether a given bit is set.

We'll see below that the code now wants to accept a VISITOR_CLONE in
addition to VISITOR_INPUT in most places, and similarly in addition to
VISITOR_OUTPUT.  Subtle.

>   */
>  typedef enum VisitorType {
> -    VISITOR_INPUT,
> -    VISITOR_OUTPUT,
> -    VISITOR_DEALLOC,
> +    VISITOR_INPUT = 1,
> +    VISITOR_OUTPUT = 2,
> +    VISITOR_CLONE = 3,
> +    VISITOR_DEALLOC = 4,
>  } VisitorType;
>
>  struct Visitor
[...]
   /*** 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.

What about the clone visitor?

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

What about the clone visitor?

    *
    * 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
    * with the same @obj 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);
[...]
   /*** Visiting lists ***/

   /*
    * Start visiting a list.
    *
    * @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.

What about the clone visitor?

    *
    * @errp obeys typical error usage, and reports failures such as a
    * member @name is not present, or present but not a list.  On error,
    * input visitors set *@list to NULL.

What about the clone visitor?

    *
    * After visit_start_list() succeeds, the caller may visit its members
    * 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 with the
    * same @list to clean up, even if intermediate visits fail.  See the
    * examples above.
    */
   void visit_start_list(Visitor *v, const char *name, GenericList **list,
                         size_t size, Error **errp);
[...]
   /*** Visiting alternates ***/

   /*
    * Start the visit of an alternate @obj.
    *
    * @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 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.

What about the clone visitor?

    *
    * If @promote_int, treat integers as QTYPE_FLOAT.
    *
    * If successful, this must be paired with visit_end_alternate() with
    * the same @obj to clean up, even if visiting the contents of the
    * alternate fails.
    */
[...]
   /*** 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.

I guess this is correct for the clone visitor.

    */
   bool visit_optional(Visitor *v, const char *name, bool *present);

   /*
    * Visit an enum value.
    *
    * @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.

I guess this is correct for the clone visitor.

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

   /*
    * Check if visitor is an input visitor.

Does the clone visitor count as input visitor here?  Should it?

    */
   bool visit_is_input(Visitor *v);

   /*** 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.

I guess this is correct for the clone visitor.

    */
   void visit_type_int(Visitor *v, const char *name, int64_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.

I guess this is correct for the clone visitor.

    */
   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 "".

I guess this is correct for the clone visitor.

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

I guess this is correct for the clone visitor.

    */
   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.

Fine, as the clone visitor doesn't support any.

    */
   void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);

> diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h
> new file mode 100644
> index 0000000..16ceff5
> --- /dev/null
> +++ b/include/qapi/clone-visitor.h
> @@ -0,0 +1,37 @@
> +/*
> + * Clone Visitor
> + *
> + * Copyright (C) 2016 Red Hat, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#ifndef QAPI_CLONE_VISITOR_H
> +#define QAPI_CLONE_VISITOR_H
> +
> +#include "qapi/visitor.h"
> +
> +/*
> + * The clone visitor is for direct use only by the QAPI_CLONE() macro;
> + * it requires that the root visit occur on an object, list, or
> + * alternate, and is not usable directly on built-in QAPI types.
> + */
> +typedef struct QapiCloneVisitor QapiCloneVisitor;
> +
> +void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
> +                                                     void **, Error **));
> +
> +/*
> + * Deep-clone QAPI object @src of the given @type, and return the result.
> + *
> + * Not usable on QAPI scalars (integers, strings, enums), nor on a
> + * QAPI object that references the 'any' type.  Safe when @src is NULL.
> + */
> +#define QAPI_CLONE(type, src)                                           \
> +    ((type *)qapi_clone(src,                                            \
> +                        (void (*)(Visitor *, const char *, void**,      \
> +                                  Error **))visit_type_ ## type))

This casts visit_type_FOO(), where FOO is a QAPI-generated complex or
alternate type, from

    void (*)(Visitor *, const char *, FOO **, Error **)

to

    void (*)(Visitor *, const char *, void **, Error **)

Okay.  Can't see how to avoid the type-punning without generating lots
of wrapper functions.

Note that scalar QAPI types have one * less: FOO * instead of FOO **.
But they're explicitly not permitted here.

> +
> +#endif
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 279ea8e..c5bdca2 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c

As we'll see further down, @obj points into the clone, except at the
root, where it points to qapi_clone()'s local variable @dst.  A
pointer-valued *@obj still points into the source.

Now let's go through the v->type checks real slow.

First one:

   void visit_complete(Visitor *v, void *opaque)
   {
       if (v->type == VISITOR_OUTPUT) {
           assert(v->complete);
       }

An output visitor needs a complete(), because without it, there's no way
to get the output (unless you resort to side effects).
qapi-visit-core.c chooses to enforce this.  Not really necessary,
because qapi-visit-core.c works just fine without it.

The clone visitor has no use for complete, because it returns its output
differently: qapi_clone() returns it.  Therefore, we don't want to treat
clone as output here.  Okay.

> @@ -44,10 +44,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
>
>      if (obj) {
>          assert(size);
> -        assert(v->type != VISITOR_OUTPUT || *obj);
> +        assert(!(v->type & VISITOR_OUTPUT) || *obj);
>      }

For real walks (obj != NULL):

* Input visitors write *obj, and don't care for the old value.

* Output visitors read *obj, and a struct can't be null.

* The dealloc visitor reads *obj, but null is fine (partially
  constructed object).

* The clone visitor reads like an output visitor (except at the root)
  and writes like an input visitor.

Before the patch, we assert "if output visitor, then *obj isn't null".

After the patch, we do the same for the clone visitor.  Correct, except
at the root.  There, @obj points to qapi_clone()'s @dst, which is
uninitialized.  I'm afraid this assertion fails if @dst happens to be
null.

Can we fix this by removing the "except at the root" special case?
Change qapi_clone to initialize dst = src, drop QapiCloneVisitor member
@root and qapi_clone_visitor_new() parameter @src.

>      v->start_struct(v, name, obj, size, &err);
> -    if (obj && v->type == VISITOR_INPUT) {
> +    if (obj && (v->type & VISITOR_INPUT)) {
>          assert(!err != !*obj);
>      }

Before the patch, we assert "input visitor must either fail or create
*obj for a real walk."

After the patch, we do the same for the clone visitor.  Okay.

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

Likewise.

>      error_propagate(errp, err);
> @@ -96,11 +96,11 @@ void visit_start_alternate(Visitor *v, const char *name,
>      Error *err = NULL;
>
>      assert(obj && size >= sizeof(GenericAlternate));
> -    assert(v->type != VISITOR_OUTPUT || *obj);
> +    assert(!(v->type & VISITOR_OUTPUT) || *obj);
>      if (v->start_alternate) {
>          v->start_alternate(v, name, obj, size, promote_int, &err);
>      }
> -    if (v->type == VISITOR_INPUT) {
> +    if (v->type & VISITOR_INPUT) {
>          assert(v->start_alternate && !err != !*obj);
>      }
>      error_propagate(errp, err);

Same analysis as for visit_start_struct().

[...]
   bool visit_is_input(Visitor *v)
   {
       return v->type == VISITOR_INPUT;
   }

This answers my question "Does the clone visitor count as input visitor
here?"  Remaining question: "Should it?"

> @@ -252,9 +252,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>      assert(obj);
>      /* TODO: Fix callers to not pass NULL when they mean "", so that we
>       * can enable:
> -    assert(v->type != VISITOR_OUTPUT || *obj);
> +    assert(!(v->type & VISITOR_OUTPUT) || *obj);
>       */
>      v->type_str(v, name, obj, &err);
> +    /* Likewise, use of NULL means we can't do (v->type & VISITOR_INPUT) */
>      if (v->type == VISITOR_INPUT) {
>          assert(!err != !*obj);
>      }

If your head doesn't hurt by know, you either wrote this, or you're not
reading closely :)

If the TODOs were already addressed, we'd again get the same analysis as
for visit_start_struct(), except for the arguments about the root, which
don't apply here, because the clone visitor doesn't accept scalar roots.

In the current state, the analysis needs to be modified as follows.

First assertion:

Before the patch, we'd like to assert "if output or clone visitor, then
*obj isn't null".  We can't as long as we need to treat null as the
empty string.

After the patch, the situation is the same for the clone visitor.  Okay.

Second assertion:

Before the patch, we assert "input visitor must either fail or create
*obj for a real walk."  The TODO doesn't apply; we create "", not null.

After the patch, we'd like to assert the same for the clone visitor, but
we can't: the clone of null is null.  Okay.

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

v->type_any() will crash for the clone visitor, so these changes aren't.
necessary.  If you want them to future-proof the code, I need to
convince myself the changes make sense, similar to what I did for the
other ones in this file.

> @@ -342,4 +343,5 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
>      } else if (v->type == VISITOR_OUTPUT) {
>          output_type_enum(v, name, obj, strings, errp);
>      }
> +    /* dealloc and clone visitors have nothing to do */
>  }

I'm upgrade my verdict from "subtle" to "scarily subtle" %-}

> diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c
> new file mode 100644
> index 0000000..a24a258
> --- /dev/null
> +++ b/qapi/qapi-clone-visitor.c
> @@ -0,0 +1,178 @@
> +/*
> + * Copy one QAPI object to another
> + *
> + * Copyright (C) 2016 Red Hat, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/clone-visitor.h"
> +#include "qapi/visitor-impl.h"
> +#include "qapi/error.h"
> +
> +struct QapiCloneVisitor {
> +    Visitor visitor;
> +    const void *root; /* Must be object, alternate, or list */
> +    size_t depth;
> +};
> +
> +static QapiCloneVisitor *to_qcv(Visitor *v)
> +{
> +    return container_of(v, QapiCloneVisitor, visitor);
> +}
> +
> +static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj,
> +                                    size_t size, Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    if (!obj) {
> +        /* Only possible when visiting an alternate's object
> +         * branch. Nothing to do here, since the earlier
> +         * visit_start_alternate() already copied memory. */

Should visitor-impl.h explain how method start_struct() is used with
alternates?  I once again forgot how this works...  Hmm, you explained
it to me during review of v3.

Despite there's "nothing to do here", you found something to do:

> +        assert(qcv->depth);
> +        return;
> +    }
> +
> +    *obj = g_memdup(qcv->depth ? *obj : qcv->root, size);
> +    qcv->depth++;
> +}
> +
> +static void qapi_clone_end(Visitor *v, void **obj)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    if (obj) {
> +        qcv->depth--;
> +    }
> +}
> +
> +static void qapi_clone_start_list(Visitor *v, const char *name,
> +                                  GenericList **listp, size_t size,
> +                                  Error **errp)
> +{
> +    qapi_clone_start_struct(v, name, (void **)listp, size, errp);
> +}
> +
> +static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail,
> +                                         size_t size)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Unshare the tail of the list cloned by g_memdup */

Humor me: g_memdup()

> +    tail->next = g_memdup(tail->next, size);
> +    return tail->next;
> +}
> +
> +static void qapi_clone_start_alternate(Visitor *v, const char *name,
> +                                       GenericAlternate **obj, size_t size,
> +                                       bool promote_int, Error **errp)
> +{
> +    qapi_clone_start_struct(v, name, (void **)obj, size, errp);
> +}
> +
> +static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj,
> +                                   Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_uint64(Visitor *v, const char *name,
> +                                    uint64_t *obj, Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj,
> +                                  Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_str(Visitor *v, const char *name, char **obj,
> +                                 Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Pointer was already cloned by g_memdup; create fresh copy */
> +    *obj = g_strdup(*obj);
> +}
> +
> +static void qapi_clone_type_number(Visitor *v, const char *name, double *obj,
> +                                    Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Value was already cloned by g_memdup */
> +}
> +
> +static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp)
> +{
> +    QapiCloneVisitor *qcv = to_qcv(v);
> +
> +    assert(qcv->depth);
> +    /* Nothing to do */
> +}
> +
> +static void qapi_clone_free(Visitor *v)
> +{
> +    g_free(v);
> +}
> +
> +static Visitor *qapi_clone_visitor_new(const void *src)
> +{
> +    QapiCloneVisitor *v;
> +
> +    v = g_malloc0(sizeof(*v));
> +    v->root = src;
> +
> +    v->visitor.type = VISITOR_CLONE;
> +    v->visitor.start_struct = qapi_clone_start_struct;
> +    v->visitor.end_struct = qapi_clone_end;
> +    v->visitor.start_list = qapi_clone_start_list;
> +    v->visitor.next_list = qapi_clone_next_list;
> +    v->visitor.end_list = qapi_clone_end;
> +    v->visitor.start_alternate = qapi_clone_start_alternate;
> +    v->visitor.end_alternate = qapi_clone_end;
> +    v->visitor.type_int64 = qapi_clone_type_int64;
> +    v->visitor.type_uint64 = qapi_clone_type_uint64;
> +    v->visitor.type_bool = qapi_clone_type_bool;
> +    v->visitor.type_str = qapi_clone_type_str;
> +    v->visitor.type_number = qapi_clone_type_number;
> +    v->visitor.type_null = qapi_clone_type_null;
> +    v->visitor.free = qapi_clone_free;
> +
> +    return &v->visitor;
> +}
> +
> +void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *,
> +                                                     void **, Error **))
> +{
> +    Visitor *v;
> +    void *dst;
> +
> +    if (!src) {
> +        return NULL;
> +    }
> +
> +    v = qapi_clone_visitor_new(src);
> +    visit_type(v, NULL, &dst, &error_abort);
> +    visit_free(v);
> +    return dst;
> +}
[Skipping the tests for now to get this review out today...]

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

* Re: [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping Eric Blake
@ 2016-06-02 14:53   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-02 14:53 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Amit Shah, Juan Quintela, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> Pull out a new qstring_append_json_string() helper, so that all
> JSON output producers can use the same output escaping rules.
>
> While it appears that vmstate's use of the simpler qjson.c
> formatter is not currently encountering any string that needs
> escapes to be valid JSON, it is better to be safe than sorry,
> and this substitution is not adding any potential asserts
> during migration.
>
> Adding a return value will let callers that care diagnose the
> situation where an attempt was made to output invalid JSON (we
> use substitute characters, and in fact guarantee that our
> output is ASCII via escapes even if the input was UTF-8).  None
> of the current callers care, but a future patch wants to make
> it possible to turn this situation into an error.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> @@ -290,3 +238,68 @@ QString *qobject_to_json_pretty(const QObject *obj)
>
>      return str;
>  }
> +
> +/**
> + * Append a JSON string to @qstring that encodes the C string @str.
> + *
> + * The JSON string is enclosed in double quotes and has funny
> + * characters escaped.  Returns -1 if encoding errors were replaced,
> + * or 0 if the input was already valid UTF-8.
> + */

Suggest:

    * @str is in UTF-8.  The JSON string is enclosed in double quotes
    * and has funny characters escaped.  Invalid UTF-8 sequences are
    * replaced by (properly escaped) U+0xFFFD replacement characters.
    * Return -1 if invalid sequences were replaced, else 0.

> +int qstring_append_json_string(QString *qstring, const char *str)
> +{
> +    const char *ptr;
> +    int cp;
> +    char buf[16];
> +    char *end;
> +    int result = 0;
> +
> +    qstring_append(qstring, "\"");
> +
> +    for (ptr = str; *ptr; ptr = end) {
> +        cp = mod_utf8_codepoint(ptr, 6, &end);
> +        switch (cp) {
> +        case '\"':
> +            qstring_append(qstring, "\\\"");
> +            break;
> +        case '\\':
> +            qstring_append(qstring, "\\\\");
> +            break;
> +        case '\b':
> +            qstring_append(qstring, "\\b");
> +            break;
> +        case '\f':
> +            qstring_append(qstring, "\\f");
> +            break;
> +        case '\n':
> +            qstring_append(qstring, "\\n");
> +            break;
> +        case '\r':
> +            qstring_append(qstring, "\\r");
> +            break;
> +        case '\t':
> +            qstring_append(qstring, "\\t");
> +            break;
> +        default:
> +            if (cp < 0) {
> +                cp = 0xFFFD; /* replacement character */
> +                result = -1;
> +            }
> +            if (cp > 0xFFFF) {
> +                /* beyond BMP; need a surrogate pair */
> +                snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
> +                         0xD800 + ((cp - 0x10000) >> 10),
> +                         0xDC00 + ((cp - 0x10000) & 0x3FF));
> +            } else if (cp < 0x20 || cp >= 0x7F) {
> +                snprintf(buf, sizeof(buf), "\\u%04X", cp);
> +            } else {
> +                buf[0] = cp;
> +                buf[1] = 0;
> +            }
> +            qstring_append(qstring, buf);
> +        }
> +    };
> +
> +    qstring_append(qstring, "\"");
> +    return result;
> +}

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting Eric Blake
@ 2016-06-02 15:02   ` Markus Armbruster
  2016-06-02 15:06     ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-02 15:02 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> Pull out a new qstring_append_json_number() helper, so that all
> JSON output producers can use a consistent style for printing
> floating point without duplicating code (since we are doing more
> data massaging than a simple printf format can handle).  (For
> now, there is only one client, but later patches will use it.)
>
> Adding a return value will let callers that care diagnose the
> situation where an attempt was made to output invalid JSON (which
> does not specify Infinity or NaN). None of the current callers
> care, but a future patch wants to make it possible to turn this
> situation into an error.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> @@ -303,3 +282,39 @@ int qstring_append_json_string(QString *qstring, const char *str)
>      qstring_append(qstring, "\"");
>      return result;
>  }
> +
> +/**
> + * Append a JSON representation of @number to @qstring.
> + *
> + * Returns -1 if the added text is not strict JSON, or 0 if the number
> + * was finite.
> + */

Suggest:

 * Return 0 if the number is finite, as required by RFC 7159, else -1.

The return value makes some sense only for symmetry with
qstring_append_json_string().  Without that, I'd ask you to keep this
function simple.  Callers could just as easily test isfinite()
themselves.

> +int qstring_append_json_number(QString *qstring, double number)
> +{
> +    char buffer[1024];
> +    int len;
> +
> +    /* FIXME: snprintf() is locale dependent; but JSON requires
> +     * numbers to be formatted as if in the C locale. Dependence
> +     * on C locale is a pervasive issue in QEMU. */
> +    /* FIXME: This risks printing Inf or NaN, which are not valid
> +     * JSON values. */
> +    /* FIXME: the default precision of 6 for %f often causes
> +     * rounding errors; we should be using DBL_DECIMAL_DIG (17),
> +     * and only rounding to a shorter number if the result would
> +     * still produce the same floating point value.  */
> +    len = snprintf(buffer, sizeof(buffer), "%f", number);
> +    assert(len > 0 && len < sizeof(buffer));
> +    while (len > 0 && buffer[len - 1] == '0') {
> +        len--;
> +    }
> +
> +    if (len && buffer[len - 1] == '.') {
> +        buffer[len - 1] = 0;
> +    } else {
> +        buffer[len] = 0;
> +    }
> +
> +    qstring_append(qstring, buffer);
> +    return isfinite(number) ? 0 : -1;
> +}

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-06-02 15:02   ` Markus Armbruster
@ 2016-06-02 15:06     ` Eric Blake
  2016-06-03  9:02       ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-06-02 15:06 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Luiz Capitulino

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

On 06/02/2016 09:02 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Pull out a new qstring_append_json_number() helper, so that all
>> JSON output producers can use a consistent style for printing
>> floating point without duplicating code (since we are doing more
>> data massaging than a simple printf format can handle).  (For
>> now, there is only one client, but later patches will use it.)
>>
>> Adding a return value will let callers that care diagnose the
>> situation where an attempt was made to output invalid JSON (which
>> does not specify Infinity or NaN). None of the current callers
>> care, but a future patch wants to make it possible to turn this
>> situation into an error.

> 
> Suggest:
> 
>  * Return 0 if the number is finite, as required by RFC 7159, else -1.
> 
> The return value makes some sense only for symmetry with
> qstring_append_json_string().  Without that, I'd ask you to keep this
> function simple.  Callers could just as easily test isfinite()
> themselves.

I'm actually thinking of modifying this, given the recent thread
pointing out that libvirt chokes hard on JSON extensions:

https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg04398.html

That is, for symmetry with qstring_append_json_string(), I'm thinking of
changing NaN to 0 and Inf to DBL_MAX, and always outputting a finite
value, in addition to returning -1 to inform the caller that a
substitution was made, so that the output is always strict JSON.

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

* Re: [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str()
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str() Eric Blake
@ 2016-06-02 15:21   ` Markus Armbruster
  2016-06-09 16:31     ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-02 15:21 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core,
	Chrysostomos Nanakos, Jeff Cody, Luiz Capitulino, Max Reitz

Eric Blake <eblake@redhat.com> writes:

> Several spots in the code malloc a string, then wrap it in a
> QString, which has to duplicate the input.  Adding a new
> constructor with transfer semantics makes this pattern more
> efficient, comparable to the just-added transfer semantics to
> go from QString back to raw string.  Use the new
> qstring_wrap_str() where it makes sense.
>
> The new test still passes under valgrind.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: new patch
> ---
>  include/qapi/qmp/qstring.h |  1 +
>  block.c                    |  3 ++-
>  block/archipelago.c        |  6 ++----
>  blockdev.c                 |  3 +--
>  qobject/qstring.c          | 25 +++++++++++++++++++++++++
>  tests/check-qstring.c      | 24 ++++++++++++++++++++++++
>  6 files changed, 55 insertions(+), 7 deletions(-)
>
> diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h
> index 2d55c87..97cf776 100644
> --- a/include/qapi/qmp/qstring.h
> +++ b/include/qapi/qmp/qstring.h
> @@ -25,6 +25,7 @@ typedef struct QString {
>  QString *qstring_new(void);
>  QString *qstring_from_str(const char *str);
>  QString *qstring_from_substr(const char *str, int start, int end);
> +QString *qstring_wrap_str(char *str);
>  size_t qstring_get_length(const QString *qstring);
>  const char *qstring_get_str(const QString *qstring);
>  char *qstring_consume_str(QString *qstring);
> diff --git a/block.c b/block.c
> index 551832f..32d06b5 100644
> --- a/block.c
> +++ b/block.c
> @@ -1448,7 +1448,8 @@ static int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags,
>      qdict_put(snapshot_options, "file.driver",
>                qstring_from_str("file"));
>      qdict_put(snapshot_options, "file.filename",
> -              qstring_from_str(tmp_filename));
> +              qstring_wrap_str(tmp_filename));
> +    tmp_filename = NULL;
>      qdict_put(snapshot_options, "driver",
>                qstring_from_str("qcow2"));
>

The g_free(tmp_filename) stays, because there are error paths bypassing
this spot.  Good.

> diff --git a/block/archipelago.c b/block/archipelago.c
> index 104f2f9..bfdc14c 100644
> --- a/block/archipelago.c
> +++ b/block/archipelago.c
> @@ -426,13 +426,11 @@ static void archipelago_parse_filename(const char *filename, QDict *options,
>      parse_filename_opts(filename, errp, &volume, &segment_name, &mport, &vport);
>
>      if (volume) {
> -        qdict_put(options, ARCHIPELAGO_OPT_VOLUME, qstring_from_str(volume));
> -        g_free(volume);
> +        qdict_put(options, ARCHIPELAGO_OPT_VOLUME, qstring_wrap_str(volume));
>      }
>      if (segment_name) {
>          qdict_put(options, ARCHIPELAGO_OPT_SEGMENT,
> -                  qstring_from_str(segment_name));
> -        g_free(segment_name);
> +                  qstring_wrap_str(segment_name));
>      }
>      if (mport != NoPort) {
>          qdict_put(options, ARCHIPELAGO_OPT_MPORT, qint_from_int(mport));
> diff --git a/blockdev.c b/blockdev.c
> index adc5058..f293cb3 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -1089,8 +1089,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
>              new_id = g_strdup_printf("%s%s%i", if_name[type],
>                                       mediastr, unit_id);
>          }
> -        qdict_put(bs_opts, "id", qstring_from_str(new_id));
> -        g_free(new_id);
> +        qdict_put(bs_opts, "id", qstring_wrap_str(new_id));
>      }
>
>      /* Add virtio block device */
> diff --git a/qobject/qstring.c b/qobject/qstring.c
> index 7a438e9..2af1914 100644
> --- a/qobject/qstring.c
> +++ b/qobject/qstring.c
> @@ -66,6 +66,31 @@ QString *qstring_from_str(const char *str)
>      return qstring_from_substr(str, 0, strlen(str) - 1);
>  }
>
> +/**
> + * qstring_wrap_str(): Create a new QString around a malloc'd C string
> + *
> + * Returns a strong reference, and caller must not use @str any more.
> + * @str may be NULL, in which case the QString will be "".

I'm not fond of conflating null pointers and "" (see also the trouble
with visit_type_str()).  For what it's worth, qstring_from_str(NULL)
crashes.  Can we reject null?

> + */
> +QString *qstring_wrap_str(char *str)
> +{
> +    QString *qstring;
> +
> +    qstring = g_malloc(sizeof(*qstring));
> +    qobject_init(QOBJECT(qstring), QTYPE_QSTRING);
> +
> +    if (str) {
> +        qstring->string = str;
> +        qstring->length = strlen(str);
> +    } else {
> +        qstring->string = g_strdup("");
> +        qstring->length = 0;
> +    }
> +    qstring->capacity = qstring->length;
> +
> +    return qstring;
> +}
> +
>  static void capacity_increase(QString *qstring, size_t len)
>  {
>      if (qstring->capacity < (qstring->length + len)) {
> diff --git a/tests/check-qstring.c b/tests/check-qstring.c
> index e6f58e0..0e2e9bd 100644
> --- a/tests/check-qstring.c
> +++ b/tests/check-qstring.c
> @@ -35,6 +35,29 @@ static void qstring_from_str_test(void)
>      QDECREF(qstring);
>  }
>
> +static void qstring_wrap_str_test(void)
> +{
> +    QString *qstring;
> +    char *str = g_strdup("QEMU");
> +
> +    qstring = qstring_wrap_str(str);
> +    g_assert(qstring != NULL);
> +    g_assert(qstring->base.refcnt == 1);
> +    g_assert(strcmp("QEMU", qstring->string) == 0);
> +    g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
> +
> +    QDECREF(qstring);
> +
> +    str = NULL;
> +    qstring = qstring_wrap_str(str);
> +    g_assert(qstring != NULL);
> +    g_assert(qstring->base.refcnt == 1);
> +    g_assert(strcmp("", qstring->string) == 0);
> +    g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
> +
> +    QDECREF(qstring);
> +}
> +
>  static void qstring_destroy_test(void)
>  {
>      QString *qstring = qstring_from_str("destroy test");
> @@ -120,6 +143,7 @@ int main(int argc, char **argv)
>      g_test_init(&argc, &argv, NULL);
>
>      g_test_add_func("/public/from_str", qstring_from_str_test);
> +    g_test_add_func("/public/wrap_str", qstring_wrap_str_test);
>      g_test_add_func("/public/destroy", qstring_destroy_test);
>      g_test_add_func("/public/get_str", qstring_get_str_test);
>      g_test_add_func("/public/append_chr", qstring_append_chr_test);

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

* Re: [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls Eric Blake
@ 2016-06-02 15:32   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-02 15:32 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core, Michael Roth,
	Luiz Capitulino, Max Reitz

Eric Blake <eblake@redhat.com> writes:

> It's simpler to have a single conversion function that takes a
> bool parameter, rather than two functions where the choice of
> function determines an internal bool.  Similar to commit fc471c18.
>
> While at it, the conversion currently cannot fail (maybe it SHOULD
> be possible to choose to fail, when encountering invalid UTF-8
> encoding or an Infinity or NaN valued double, but that's a story
> for another day), so clean up callers to avoid a needless assert.
> And use bool rather than int for 'pretty'.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
[...]
> diff --git a/tests/check-qobject-json.c b/tests/check-qobject-json.c
> index d889501..9814282 100644
> --- a/tests/check-qobject-json.c
> +++ b/tests/check-qobject-json.c
> @@ -64,7 +64,7 @@ static void escaped_string(void)
>          g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);
>
>          if (test_cases[i].skip == 0) {
> -            str = qobject_to_json(obj);
> +            str = qobject_to_json(obj, false);
>              g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
>              qobject_decref(obj);
>          }
> @@ -98,7 +98,7 @@ static void simple_string(void)
>          str = qobject_to_qstring(obj);
>          g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
>
> -        str = qobject_to_json(obj);
> +        str = qobject_to_json(obj, false);
>          g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
>
>          qobject_decref(obj);
> @@ -832,13 +832,8 @@ static void utf8_string(void)
>          qobject_decref(obj);
>
>          obj = QOBJECT(qstring_from_str(utf8_in));
> -        str = qobject_to_json(obj);
> -        if (json_out) {
> -            g_assert(str);
> -            g_assert_cmpstr(qstring_get_str(str), ==, json_out);
> -        } else {

Unreachable, because json_out can be null only when
test_cases[o].json_in is, and null json_in would've crashed in
qobject_from_json().

> -            g_assert(!str);
> -        }
> +        str = qobject_to_json(obj, false);
> +        g_assert_cmpstr(qstring_get_str(str), ==, json_out);
>          QDECREF(str);
>          qobject_decref(obj);
>

Thanks for cleaning up my mess.

[...]

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

* Re: [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor Eric Blake
@ 2016-06-03  7:39   ` Markus Armbruster
  2016-06-03 12:53     ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  7:39 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> We have several places that want to go from qapi to JSON; right now,
> they have to create an intermediate QObject to do the work.  That
> also has the drawback that the JSON formatting of a QDict will
> rearrange keys (according to a deterministic, but unpredictable,
> hash), when humans have an easier time if dicts are produced in
> the same order as the qapi type.
>
> For these reasons, it is time to add a new JSON output visitor.
> This patch just adds the basic visitor and tests that it works;
> later patches will add pretty-printing, support for visit_type_any(),
> and conversion of clients to use the visitor.
>
> Design choices: Unlike the QMP output visitor, the JSON visitor
> refuses to visit a required string with a NULL value, via abort().
> Reusing QString to grow the contents means that we easily share
> code with both qobject-json.c and qjson.c; although it might be
> nice to enhance things to take an optional output callback
> function so that the output can truly be streamed instead of
> collected in memory.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: retitle, split off inf/NaN handling, rebase to visit_complete
> and visit_free changes, defer type_any, address other findings from
> Markus
> v3: retitle, rebase to master, minor cleanups
> v2: rebase to qapi subset E v8; add test of error outputting
> infinity; use unsigned depth
> ---
>  include/qapi/visitor.h             |  33 ++--
>  include/qapi/json-output-visitor.h |  28 +++
>  qapi/json-output-visitor.c         | 193 +++++++++++++++++++
>  tests/test-json-output-visitor.c   | 383 +++++++++++++++++++++++++++++++++++++
>  tests/test-qmp-output-visitor.c    |   5 +
>  qapi/Makefile.objs                 |   2 +-
>  tests/.gitignore                   |   1 +
>  tests/Makefile                     |   5 +-
>  8 files changed, 632 insertions(+), 18 deletions(-)
>  create mode 100644 include/qapi/json-output-visitor.h
>  create mode 100644 qapi/json-output-visitor.c
>  create mode 100644 tests/test-json-output-visitor.c
>
> diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
> index 3f46921..c097507 100644
> --- a/include/qapi/visitor.h
> +++ b/include/qapi/visitor.h
> @@ -26,17 +26,17 @@
>   *
>   * There are four 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, the
> - * dealloc visitor can take a QAPI graph (possibly partially
> - * constructed) and recursively free its resources, and the clone
> - * visitor performs a deep clone of one QAPI object to another.  While
> - * the dealloc and QMP input/output visitors are general, the string,
> - * QemuOpts, and clone 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.
> + * the corresponding QAPI graph, output visitors (QMP, string, and
> + * JSON) take a completed QAPI graph and generate an external
> + * representation, the dealloc visitor can take a QAPI graph (possibly
> + * partially constructed) and recursively free its resources, and the
> + * clone visitor performs a deep clone of one QAPI object to another.
> + * While the dealloc, JSON output, and QMP input/output visitors are
> + * general, the string, QemuOpts, and clone 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 of the visitors are created via:
>   *
> @@ -59,11 +59,12 @@
>   * 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.
> + * The @name parameter of visit_type_FOO() and visit_start_OBJECT()
> + * 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.

Not related to this patch, just came up in review of v3.  Splitting it
out into its own patch doesn't seem worthwhile, so let's keep it here.

>   *
>   * 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
> diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
> new file mode 100644
> index 0000000..41c79f4
> --- /dev/null
> +++ b/include/qapi/json-output-visitor.h
> @@ -0,0 +1,28 @@
> +/*
> + * JSON Output Visitor
> + *
> + * Copyright (C) 2015-2016 Red Hat, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#ifndef JSON_OUTPUT_VISITOR_H
> +#define JSON_OUTPUT_VISITOR_H
> +
> +#include "qapi/visitor.h"
> +
> +typedef struct JsonOutputVisitor JsonOutputVisitor;
> +
> +/*
> + * Create a new JSON output visitor.
> + *
> + * If everything else succeeds, pass @result to visit_complete() to
> + * collect the result of the visit.
> + *
> + * For now, this cannot be used to visit the 'any' type.
> + */
> +Visitor *json_output_visitor_new(char **result);
> +
> +#endif
> diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
> new file mode 100644
> index 0000000..7010ff8
> --- /dev/null
> +++ b/qapi/json-output-visitor.c
> @@ -0,0 +1,193 @@
> +/*
> + * Convert QAPI to JSON
> + *
> + * Copyright (C) 2015-2016 Red Hat, Inc.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/json-output-visitor.h"
> +#include "qapi/visitor-impl.h"
> +#include "qapi/qmp/qstring.h"
> +#include "qapi/qmp/qobject-json.h"
> +
> +struct JsonOutputVisitor {
> +    Visitor visitor;
> +    QString *str;
> +    bool comma;
> +    unsigned int depth;
> +    char **result;
> +};
> +
> +static JsonOutputVisitor *to_jov(Visitor *v)
> +{
> +    return container_of(v, JsonOutputVisitor, visitor);
> +}
> +
> +static void json_output_name(JsonOutputVisitor *jov, const char *name)
> +{
> +    if (jov->comma) {
> +        qstring_append(jov->str, ", ");
> +    } else {
> +        jov->comma = true;
> +    }
> +    if (name && jov->depth) {
> +        qstring_append_json_string(jov->str, name);
> +        qstring_append(jov->str, ": ");
> +    }
> +}
> +
> +static void json_output_start_struct(Visitor *v, const char *name, void **obj,
> +                                     size_t unused, Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    qstring_append(jov->str, "{");
> +    jov->comma = false;
> +    jov->depth++;
> +}
> +
> +static void json_output_end_struct(Visitor *v, void **obj)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    assert(jov->depth);
> +    jov->depth--;
> +    qstring_append(jov->str, "}");
> +    jov->comma = true;
> +}
> +
> +static void json_output_start_list(Visitor *v, const char *name,
> +                                   GenericList **listp, size_t size,
> +                                   Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    qstring_append(jov->str, "[");
> +    jov->comma = false;
> +    jov->depth++;
> +}
> +
> +static GenericList *json_output_next_list(Visitor *v, GenericList *tail,
> +                                          size_t size)
> +{
> +    return tail->next;
> +}
> +
> +static void json_output_end_list(Visitor *v, void **obj)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    assert(jov->depth);
> +    jov->depth--;
> +    qstring_append(jov->str, "]");
> +    jov->comma = true;
> +}
> +
> +static void json_output_type_int64(Visitor *v, const char *name, int64_t *obj,
> +                                   Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    qstring_append_printf(jov->str, "%" PRId64, *obj);
> +}
> +
> +static void json_output_type_uint64(Visitor *v, const char *name,
> +                                    uint64_t *obj, Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    qstring_append_printf(jov->str, "%" PRIu64, *obj);
> +}
> +
> +static void json_output_type_bool(Visitor *v, const char *name, bool *obj,
> +                                  Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    qstring_append(jov->str, *obj ? "true" : "false");
> +}
> +
> +static void json_output_type_str(Visitor *v, const char *name, char **obj,
> +                                 Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    assert(*obj);
> +    json_output_name(jov, name);
> +    /* FIXME: report invalid UTF-8 encoding */
> +    qstring_append_json_string(jov->str, *obj);
> +}
> +
> +static void json_output_type_number(Visitor *v, const char *name, double *obj,
> +                                    Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    /* FIXME: report Inf/NaN problems */
> +    qstring_append_json_number(jov->str, *obj);
> +}
> +
> +static void json_output_type_null(Visitor *v, const char *name, Error **errp)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    json_output_name(jov, name);
> +    qstring_append(jov->str, "null");
> +}
> +
> +static void json_output_complete(Visitor *v, void *result)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    assert(!jov->depth);
> +    assert(qstring_get_length(jov->str));
> +    assert(jov->result == result);
> +    *jov->result = qstring_consume_str(jov->str);
> +    jov->str = NULL;
> +    jov->result = NULL;
> +}

Related: discussion of complete() semantics in review of PATCH 12.
Non-idempotent semantics save us a copy, like in
string_output_complete().

> +
> +static void json_output_free(Visitor *v)
> +{
> +    JsonOutputVisitor *jov = to_jov(v);
> +
> +    QDECREF(jov->str);
> +    g_free(jov);

I'm afraid this leaks jov->result when the visitor is destroyed without
calling its complete() method.

string_output_free() appears to have the same leak.

> +}
> +
> +Visitor *json_output_visitor_new(char **result)
> +{
> +    JsonOutputVisitor *v;
> +
> +    v = g_malloc0(sizeof(*v));
> +    v->result = result;
> +    *result = NULL;
> +    v->str = qstring_new();
> +
> +    v->visitor.type = VISITOR_OUTPUT;
> +    v->visitor.start_struct = json_output_start_struct;
> +    v->visitor.end_struct = json_output_end_struct;
> +    v->visitor.start_list = json_output_start_list;
> +    v->visitor.next_list = json_output_next_list;
> +    v->visitor.end_list = json_output_end_list;
> +    v->visitor.type_int64 = json_output_type_int64;
> +    v->visitor.type_uint64 = json_output_type_uint64;
> +    v->visitor.type_bool = json_output_type_bool;
> +    v->visitor.type_str = json_output_type_str;
> +    v->visitor.type_number = json_output_type_number;
> +    v->visitor.type_null = json_output_type_null;
> +    v->visitor.complete = json_output_complete;
> +    v->visitor.free = json_output_free;
> +
> +    return &v->visitor;
> +}
[...]

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

* Re: [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in JSON output visitor
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in " Eric Blake
@ 2016-06-03  7:56   ` Markus Armbruster
  2016-06-03 12:55     ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  7:56 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Similar to pretty printing in the QObject visitor.  The trickiest
> part is probably that the testsuite now has to honor parameterization
> on whether pretty printing is enabled.

Worth mentioning that the pretty-printing matches the one in
qobject-json.c?

>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: rebase to earlier changes, defer type_any() to later
> v3: rebase to earlier changes
> v2: rebase to earlier changes
> ---
>  include/qapi/json-output-visitor.h |   5 +-
>  qapi/json-output-visitor.c         |  15 ++++-
>  tests/test-json-output-visitor.c   | 122 +++++++++++++++++++++++++------------
>  3 files changed, 99 insertions(+), 43 deletions(-)
>
> diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
> index 41c79f4..94c9e0f 100644
> --- a/include/qapi/json-output-visitor.h
> +++ b/include/qapi/json-output-visitor.h
> @@ -21,8 +21,11 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
>   * If everything else succeeds, pass @result to visit_complete() to
>   * collect the result of the visit.
>   *
> + * If @pretty, make the output legible with newlines and indentation;
> + * otherwise the output uses a single line.
> + *
>   * For now, this cannot be used to visit the 'any' type.
>   */
> -Visitor *json_output_visitor_new(char **result);
> +Visitor *json_output_visitor_new(bool pretty, char **result);
>
>  #endif
> diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
> index 7010ff8..881c9ee 100644
> --- a/qapi/json-output-visitor.c
> +++ b/qapi/json-output-visitor.c
> @@ -17,6 +17,7 @@
>  struct JsonOutputVisitor {
>      Visitor visitor;
>      QString *str;
> +    bool pretty;
>      bool comma;
>      unsigned int depth;
>      char **result;
> @@ -30,10 +31,13 @@ static JsonOutputVisitor *to_jov(Visitor *v)
>  static void json_output_name(JsonOutputVisitor *jov, const char *name)
>  {
>      if (jov->comma) {
> -        qstring_append(jov->str, ", ");
> +        qstring_append(jov->str, jov->pretty ? "," : ", ");
>      } else {
>          jov->comma = true;
>      }
> +    if (jov->pretty && jov->depth) {
> +        qstring_append_printf(jov->str, "\n%*s", 4 * jov->depth, "");
> +    }
>      if (name && jov->depth) {
>          qstring_append_json_string(jov->str, name);
>          qstring_append(jov->str, ": ");
> @@ -57,6 +61,9 @@ static void json_output_end_struct(Visitor *v, void **obj)
>
>      assert(jov->depth);
>      jov->depth--;
> +    if (jov->pretty) {
> +        qstring_append_printf(jov->str, "\n%*s", 4 * jov->depth, "");
> +    }
>      qstring_append(jov->str, "}");
>      jov->comma = true;
>  }
> @@ -85,6 +92,9 @@ static void json_output_end_list(Visitor *v, void **obj)
>
>      assert(jov->depth);
>      jov->depth--;
> +    if (jov->pretty) {
> +        qstring_append_printf(jov->str, "\n%*s", 4 * jov->depth, "");
> +    }
>      qstring_append(jov->str, "]");
>      jov->comma = true;
>  }
> @@ -165,11 +175,12 @@ static void json_output_free(Visitor *v)
>      g_free(jov);
>  }
>
> -Visitor *json_output_visitor_new(char **result)
> +Visitor *json_output_visitor_new(bool pretty, char **result)
>  {
>      JsonOutputVisitor *v;
>
>      v = g_malloc0(sizeof(*v));
> +    v->pretty = pretty;
>      v->result = result;
>      *result = NULL;
>      v->str = qstring_new();
> diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
> index 3c77a61..5073715 100644
> --- a/tests/test-json-output-visitor.c
> +++ b/tests/test-json-output-visitor.c
> @@ -24,14 +24,17 @@
>
>  typedef struct TestOutputVisitorData {
>      Visitor *ov;
> +    bool pretty;
>      char *str;
>  } TestOutputVisitorData;
>
>  static void visitor_output_setup(TestOutputVisitorData *data,
> -                                 const void *unused)
> +                                 const void *arg)
>  {
> -    data->ov = json_output_visitor_new(&data->str);
> +    const bool *pretty = arg;

Could do bool pretty = *(bool *)arg.  Matter of taste.  Same elsewhere.

Blank line between declarations and statements, please.

> +    data->ov = json_output_visitor_new(*pretty, &data->str);
>      g_assert(data->ov);
> +    data->pretty = *pretty;

I'd put this line before data->ov = ...

>  }
>
>  static void visitor_output_teardown(TestOutputVisitorData *data,
> @@ -52,8 +55,9 @@ static const char *visitor_get(TestOutputVisitorData *data)
>
>  static void visitor_reset(TestOutputVisitorData *data)
>  {
> +    bool pretty = data->pretty;

Blank line between declarations and statements, please.

>      visitor_output_teardown(data, NULL);
> -    visitor_output_setup(data, NULL);
> +    visitor_output_setup(data, &pretty);

We could pass &data->pretty (teardown leaves it alone), but I guess not
relying on that is slightly cleaner.

>  }
>
>  static void test_visitor_out_int(TestOutputVisitorData *data,
> @@ -161,8 +165,9 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
>  }
>
>  static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
> -                                           const void *unused)
> +                                           const void *arg)
>  {
> +    const bool *pretty = arg;
>      int64_t value = 42;
>      UserDefTwo *ud2;
>      const char *string = "user def string";
> @@ -192,27 +197,51 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
>      visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
>
>      out = visitor_get(data);
> -    g_assert_cmpstr(out, ==,
> -                    "{"
> -                     "\"string0\": \"forty two\", "
> -                     "\"dict1\": {"
> -                      "\"string1\": \"forty three\", "
> -                      "\"dict2\": {"
> -                       "\"userdef\": {"
> -                        "\"integer\": 42, "
> -                        "\"string\": \"user def string\""
> -                        "}, "
> -                       "\"string\": \"forty four\""
> -                       "}, "
> -                      "\"dict3\": {"
> -                       "\"userdef\": {"
> -                        "\"integer\": 42, "
> -                        "\"string\": \"user def string\""
> -                        "}, "
> -                      "\"string\": \"forty five\""
> -                      "}"
> -                     "}"
> -                    "}");
> +    if (*pretty) {
> +        g_assert_cmpstr(out, ==,
> +                        "{\n"
> +                        "    \"string0\": \"forty two\",\n"
> +                        "    \"dict1\": {\n"
> +                        "        \"string1\": \"forty three\",\n"
> +                        "        \"dict2\": {\n"
> +                        "            \"userdef\": {\n"
> +                        "                \"integer\": 42,\n"
> +                        "                \"string\": \"user def string\"\n"
> +                        "            },\n"
> +                        "            \"string\": \"forty four\"\n"
> +                        "        },\n"
> +                        "        \"dict3\": {\n"
> +                        "            \"userdef\": {\n"
> +                        "                \"integer\": 42,\n"
> +                        "                \"string\": \"user def string\"\n"
> +                        "            },\n"
> +                        "            \"string\": \"forty five\"\n"
> +                        "        }\n"
> +                        "    }\n"
> +                        "}");
> +    } else {
> +        g_assert_cmpstr(out, ==,
> +                        "{"
> +                         "\"string0\": \"forty two\", "
> +                         "\"dict1\": {"
> +                          "\"string1\": \"forty three\", "
> +                          "\"dict2\": {"
> +                           "\"userdef\": {"
> +                            "\"integer\": 42, "
> +                            "\"string\": \"user def string\""
> +                            "}, "
> +                           "\"string\": \"forty four\""
> +                           "}, "
> +                          "\"dict3\": {"
> +                           "\"userdef\": {"
> +                            "\"integer\": 42, "
> +                            "\"string\": \"user def string\""
> +                            "}, "
> +                          "\"string\": \"forty five\""
> +                          "}"
> +                         "}"
> +                        "}");
> +    }

The not-pretty case is unchanged.  Good.

>      qapi_free_UserDefTwo(ud2);
>  }
>
> @@ -346,36 +375,49 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
>      g_assert_cmpstr(out, ==, "null");
>  }
>
> -static void output_visitor_test_add(const char *testpath,
> +static void output_visitor_test_add(const char *testpath, bool *data,
>                                      void (*test_func)(TestOutputVisitorData *,
>                                                        const void *))
>  {
> -    g_test_add(testpath, TestOutputVisitorData, NULL, visitor_output_setup,
> +    g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
>                 test_func, visitor_output_teardown);
>  }
>
>  int main(int argc, char **argv)
>  {
> +    bool plain = false;
> +    bool pretty = true;
> +
>      g_test_init(&argc, &argv, NULL);
>
> -    output_visitor_test_add("/visitor/json/int", test_visitor_out_int);
> -    output_visitor_test_add("/visitor/json/bool", test_visitor_out_bool);
> -    output_visitor_test_add("/visitor/json/number", test_visitor_out_number);
> -    output_visitor_test_add("/visitor/json/string", test_visitor_out_string);
> -    output_visitor_test_add("/visitor/json/enum", test_visitor_out_enum);
> -    output_visitor_test_add("/visitor/json/enum-errors",
> +    output_visitor_test_add("/visitor/json/int", &plain,
> +                            test_visitor_out_int);
> +    output_visitor_test_add("/visitor/json/bool", &plain,
> +                            test_visitor_out_bool);
> +    output_visitor_test_add("/visitor/json/number", &plain,
> +                            test_visitor_out_number);
> +    output_visitor_test_add("/visitor/json/string", &plain,
> +                            test_visitor_out_string);
> +    output_visitor_test_add("/visitor/json/enum", &plain,
> +                            test_visitor_out_enum);
> +    output_visitor_test_add("/visitor/json/enum-errors", &plain,
>                              test_visitor_out_enum_errors);
> -    output_visitor_test_add("/visitor/json/struct", test_visitor_out_struct);
> -    output_visitor_test_add("/visitor/json/struct-nested",
> +    output_visitor_test_add("/visitor/json/struct", &plain,
> +                            test_visitor_out_struct);
> +    output_visitor_test_add("/visitor/json/struct-nested", &plain,
>                              test_visitor_out_struct_nested);
> -    output_visitor_test_add("/visitor/json/struct-errors",
> +    output_visitor_test_add("/visitor/json-pretty/struct-nested", &pretty,
> +                            test_visitor_out_struct_nested);
> +    output_visitor_test_add("/visitor/json/struct-errors", &plain,
>                              test_visitor_out_struct_errors);
> -    output_visitor_test_add("/visitor/json/list", test_visitor_out_list);
> -    output_visitor_test_add("/visitor/json/union-flat",
> +    output_visitor_test_add("/visitor/json/list", &plain,
> +                            test_visitor_out_list);
> +    output_visitor_test_add("/visitor/json/union-flat", &plain,
>                              test_visitor_out_union_flat);
> -    output_visitor_test_add("/visitor/json/alternate",
> +    output_visitor_test_add("/visitor/json/alternate", &plain,
>                              test_visitor_out_alternate);
> -    output_visitor_test_add("/visitor/json/null", test_visitor_out_null);
> +    output_visitor_test_add("/visitor/json/null", &plain,
> +                            test_visitor_out_null);
>
>      g_test_run();

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

* Re: [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor Eric Blake
@ 2016-06-03  8:25   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  8:25 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> Rather than open-code two different JSON visitors, it's nicer to
> make qobject_to_json() reuse the JSON output visitor.  This also
> lets us pass QObject to any output visitor (passing it to the
> string output visitor will fail if structs are involved, but
> passing it to the QMP output visitor would provide a way to
> deep-clone a QObject).

If I understand this correctly, the basic idea is as follows.

The JSON ouput visitor is normally used via QAPI-generated visit
functions to convert a QAPI object (in general a tree) to JSON.  This is
a "real" visit in QAPI visitor parlance.

Note that the tree walk is in the QAPI-generated visit functions, and
the actual conversion is in the JSON output visitor.  This lets us reuse
the latter with a different tree walk, namely one of a QObject (also a
tree).  This is a "virtual" visit in QAPI visitor parlance.  The result
is a replacement for qobject_to_json() that is shorter and avoids
duplicating the conversion.

We can also use this QObject tree walk with other output visitors.  The
string output visitor isn't terribly interesting.  With the (misnamed)
QMP output visitor, we get a QObject deep clone.

Makes me wonder whether we have any QObject deep-clones in the tree we
could replace now.

> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v4: new patch, inspired by discussion on v3 12/18
> ---
>  include/qapi/qmp/qobject-json.h |   9 ++++
>  qobject/qobject-json.c          | 108 +++++++++++-----------------------------
>  2 files changed, 39 insertions(+), 78 deletions(-)
>
> diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
> index e4d11cf..28e44b2 100644
> --- a/include/qapi/qmp/qobject-json.h
> +++ b/include/qapi/qmp/qobject-json.h
> @@ -24,6 +24,15 @@ QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0);
>  /* Convert the object to QString; does not fail. */
>  QString *qobject_to_json(const QObject *obj, bool pretty);
>
> +/*
> + * Visit the object with an output visitor @v.
> + *
> + * @name represents the relationship of this object to any parent
> + * (ignored for a top-level visit, must be set if part of a
> + * dictionary, should be NULL if part of a list).
> + */
> +void qobject_visit_output(Visitor *v, const char *name, const QObject *obj);
> +
>  int qstring_append_json_string(QString *qstring, const char *str);
>  int qstring_append_json_number(QString *qstring, double number);
>
> diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
> index 9ace92b..05b020a 100644
> --- a/qobject/qobject-json.c
> +++ b/qobject/qobject-json.c
> @@ -18,6 +18,9 @@
>  #include "qapi/qmp/qobject-json.h"
>  #include "qemu/unicode.h"
>  #include "qapi/qmp/types.h"
> +#include "qapi/error.h"
> +#include "qapi/visitor.h"
> +#include "qapi/json-output-visitor.h"
>  #include <math.h>
>
>  typedef struct JSONParsingState
> @@ -69,115 +72,62 @@ QObject *qobject_from_jsonf(const char *string, ...)
>      return obj;
>  }
>
> -typedef struct ToJsonIterState
> -{
> -    int indent;
> -    bool pretty;
> -    int count;
> -    QString *str;
> -} ToJsonIterState;
> -
> -static void to_json(const QObject *obj, QString *str, bool pretty, int indent);
> -
>  static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
>  {
> -    ToJsonIterState *s = opaque;
> +    Visitor *v = opaque;
>
> -    if (s->count) {
> -        qstring_append(s->str, s->pretty ? "," : ", ");
> -    }
> -
> -    if (s->pretty) {
> -        qstring_append_printf(s->str, "\n%*s", 4 * s->indent, "");
> -    }
> -
> -    qstring_append_json_string(s->str, key);
> -
> -    qstring_append(s->str, ": ");
> -    to_json(obj, s->str, s->pretty, s->indent);
> -    s->count++;
> +    qobject_visit_output(v, key, obj);
>  }

This function is now a trivial wrapper.  It's used like this:

       qdict_iter(val, to_json_dict_iter, v);

You might want to replace it by something like

       for (ent = qdict_first(val); ent; ent = qdict_next(val, ent)) {
           qobject_visit_output(v, ent->key, ent->value);
       }

I'd find that easier to understand.

>
>  static void to_json_list_iter(QObject *obj, void *opaque)
>  {
> -    ToJsonIterState *s = opaque;
> +    Visitor *v = opaque;
>
> -    if (s->count) {
> -        qstring_append(s->str, s->pretty ? "," : ", ");
> -    }
> -
> -    if (s->pretty) {
> -        qstring_append_printf(s->str, "\n%*s", 4 * s->indent, "");
> -    }
> -
> -    to_json(obj, s->str, s->pretty, s->indent);
> -    s->count++;
> +    qobject_visit_output(v, NULL, obj);
>  }

Likewise.

>
> -static void to_json(const QObject *obj, QString *str, bool pretty, int indent)
> +void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
>  {
>      switch (qobject_type(obj)) {
>      case QTYPE_QNULL:
> -        qstring_append(str, "null");
> +        visit_type_null(v, name, &error_abort);
>          break;
>      case QTYPE_QINT: {
> -        QInt *val = qobject_to_qint(obj);
> -        qstring_append_printf(str, "%" PRId64, qint_get_int(val));
> +        int64_t val = qint_get_int(qobject_to_qint(obj));
> +        visit_type_int64(v, name, &val, &error_abort);
>          break;
>      }
>      case QTYPE_QSTRING: {
> -        QString *val = qobject_to_qstring(obj);
> +        const char *str = qstring_get_str(qobject_to_qstring(obj));
>          /* FIXME: no way inform user if we modified the string to
>           * avoid encoding errors */

More precise: no way to inform the user if we replaced invalid UTF-8
sequences in the QString.

> -        qstring_append_json_string(str, qstring_get_str(val));
> +        visit_type_str(v, name, (char **)&str, &error_abort);
>          break;
>      }
>      case QTYPE_QDICT: {
> -        ToJsonIterState s;
>          QDict *val = qobject_to_qdict(obj);
> -
> -        s.count = 0;
> -        s.str = str;
> -        s.indent = indent + 1;
> -        s.pretty = pretty;
> -        qstring_append_chr(str, '{');
> -        qdict_iter(val, to_json_dict_iter, &s);
> -        if (pretty) {
> -            qstring_append_printf(str, "\n%*s", 4 * indent, "");
> -        }
> -        qstring_append_chr(str, '}');
> +        visit_start_struct(v, name, NULL, 0, &error_abort);
> +        qdict_iter(val, to_json_dict_iter, v);
> +        visit_check_struct(v, &error_abort);
> +        visit_end_struct(v, NULL);
>          break;
>      }
>      case QTYPE_QLIST: {
> -        ToJsonIterState s;
>          QList *val = qobject_to_qlist(obj);
> -
> -        s.count = 0;
> -        s.str = str;
> -        s.indent = indent + 1;
> -        s.pretty = pretty;
> -        qstring_append_chr(str, '[');
> -        qlist_iter(val, (void *)to_json_list_iter, &s);
> -        if (pretty) {
> -            qstring_append_printf(str, "\n%*s", 4 * indent, "");
> -        }
> -        qstring_append_chr(str, ']');
> +        visit_start_list(v, name, NULL, 0, &error_abort);
> +        qlist_iter(val, to_json_list_iter, v);
> +        visit_end_list(v, NULL);
>          break;
>      }
>      case QTYPE_QFLOAT: {
> -        QFloat *val = qobject_to_qfloat(obj);
> +        double val = qfloat_get_double(qobject_to_qfloat(obj));
>          /* FIXME: no way inform user if we generated invalid JSON */
> -        qstring_append_json_number(str, qfloat_get_double(val));
> +        visit_type_number(v, name, &val, NULL);
>          break;
>      }
>      case QTYPE_QBOOL: {
> -        QBool *val = qobject_to_qbool(obj);
> -
> -        if (qbool_get_bool(val)) {
> -            qstring_append(str, "true");
> -        } else {
> -            qstring_append(str, "false");
> -        }
> +        bool val = qbool_get_bool(qobject_to_qbool(obj));
> +        visit_type_bool(v, name, &val, &error_abort);
>          break;
>      }
>      default:
> @@ -187,11 +137,13 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent)
>
>  QString *qobject_to_json(const QObject *obj, bool pretty)
>  {
> -    QString *str = qstring_new();
> +    char *str;
> +    Visitor *v = json_output_visitor_new(pretty, &str);
>
> -    to_json(obj, str, pretty, 0);
> -
> -    return str;
> +    qobject_visit_output(v, NULL, obj);
> +    visit_complete(v, &str);
> +    visit_free(v);
> +    return qstring_wrap_str(str);
>  }
>
>  /**

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

* Re: [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output
  2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output Eric Blake
@ 2016-06-03  8:29   ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  8:29 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> Now that we can visit any QObject, it's easy to add support
> for visit_type_any() in the JSON output visitor.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

Unlike in v3, this actually clones the QObject behind the any type.
Much cleaner, and very little code due to your elegant use of visitors.

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

* Re: [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor
  2016-05-19 20:18   ` Eric Blake
@ 2016-06-03  8:36     ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  8:36 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core, Michael Roth,
	Luiz Capitulino, Max Reitz

Eric Blake <eblake@redhat.com> writes:

> On 05/19/2016 10:52 AM, Eric Blake wrote:
>> Let the caller decide whether output must be strict JSON (and
>> raise an error on an attempt to output an encoding error or
>> non-finite number), vs. the status quo of relaxed (encoding
>> errors are rewritten to use substitute U+fffd characters,
>> and non-finite numbers are output).
>> 
>> Adjust the testsuite to cover this: check-qobject-json checks
>> relaxed mode (since qobject_to_json() is unchanged in behavior),
>> test-qmp-output-visitor checks that QObject doesn't care about
>> JSON restrictions, and test-json-output-visitor is now in
>> strict mode and flags the errors.
>> 
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>> 
>
>> +++ b/qobject/qobject-json.c
>> @@ -72,62 +72,81 @@ QObject *qobject_from_jsonf(const char *string, ...)
>>      return obj;
>>  }
>> 
>> +typedef struct ToJson {
>> +{
>> +    Visitor *v;
>
> Uggh, posted the wrong version. checkpatch complained the { was on the
> wrong line, so I added it in the right one and then pushed before
> re-saving after deleting the duplicate one.
>
> Actually, if this patch is worthwhile, I'm thinking that in v5, I'll
> minimize some of the churn and keep the ToJsonIterState struct that I
> removed in 26/28, even if it only has a single 'Visitor *v' member at
> that time, rather than completely removing it there to reinstate it here.

I suspect you won't need it if you replace qdict_iter() by
qdict_first(), qdict_next(), and qlist_iter() by QLIST_FOREACH_ENTRY.

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-06-02 15:06     ` Eric Blake
@ 2016-06-03  9:02       ` Markus Armbruster
  2016-06-09 16:07         ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  9:02 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> On 06/02/2016 09:02 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Pull out a new qstring_append_json_number() helper, so that all
>>> JSON output producers can use a consistent style for printing
>>> floating point without duplicating code (since we are doing more
>>> data massaging than a simple printf format can handle).  (For
>>> now, there is only one client, but later patches will use it.)
>>>
>>> Adding a return value will let callers that care diagnose the
>>> situation where an attempt was made to output invalid JSON (which
>>> does not specify Infinity or NaN). None of the current callers
>>> care, but a future patch wants to make it possible to turn this
>>> situation into an error.
>
>> 
>> Suggest:
>> 
>>  * Return 0 if the number is finite, as required by RFC 7159, else -1.
>> 
>> The return value makes some sense only for symmetry with
>> qstring_append_json_string().  Without that, I'd ask you to keep this
>> function simple.  Callers could just as easily test isfinite()
>> themselves.
>
> I'm actually thinking of modifying this, given the recent thread
> pointing out that libvirt chokes hard on JSON extensions:
>
> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg04398.html
>
> That is, for symmetry with qstring_append_json_string(), I'm thinking of
> changing NaN to 0 and Inf to DBL_MAX, and always outputting a finite
> value, in addition to returning -1 to inform the caller that a
> substitution was made, so that the output is always strict JSON.

Mapping infinities to DBL_MIN and DBL_MAX is debatable, but mapping NaN
to zero is outright wrong.

If we decide QMP should stick to JSON here and avoid non-finite numbers,
we need to treat an attempt to emit a non-finite number as a bug:
assert(isfinite(...)).  Making sure nothing ever attempts to emit such
numbers will be tedious.

If we decide QMP should remain as it is, we need to document non-finite
numbers among its JSON extensions.  We should also fix our QMP parsers
to accept non-finite numbers then.  Including the one in libvirt.
Attempts to emit non-finite numbers then *may* be bugs.  Really no
different than finite numbers outside their intended range, such as a
negative size.  Catching these bugs is of course also tedious.  The
difference is that they manifest in QMP as semantic instead of lexical
errors.  Lexical errors are the worst to handle gracefully.

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

* Re: [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor
  2016-05-19 16:52 ` [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor Eric Blake
  2016-05-19 20:18   ` Eric Blake
@ 2016-06-03  9:21   ` Markus Armbruster
  1 sibling, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03  9:21 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Kevin Wolf, Michael Roth, open list:Block layer core,
	Luiz Capitulino, Max Reitz

Eric Blake <eblake@redhat.com> writes:

> Let the caller decide whether output must be strict JSON (and
> raise an error on an attempt to output an encoding error or
> non-finite number), vs. the status quo of relaxed (encoding
> errors are rewritten to use substitute U+fffd characters,
> and non-finite numbers are output).
>
> Adjust the testsuite to cover this: check-qobject-json checks
> relaxed mode (since qobject_to_json() is unchanged in behavior),
> test-qmp-output-visitor checks that QObject doesn't care about
> JSON restrictions, and test-json-output-visitor is now in
> strict mode and flags the errors.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>

We need to decide whether QMP should keep extending JSON for numbers
(see my review of PATCH 17), and how to treat invalid UTF-8.  If the
decision leads to two JSON dialects in QEMU, one strict and one QMP, we
need this patch.

> ---
> v4: split out as new patch
> [was parts of 04/18 and 07/18 in v3]
> ---
>  include/qapi/json-output-visitor.h |  7 ++++-
>  include/qapi/qmp/qobject-json.h    |  3 +-
>  qapi/json-output-visitor.c         | 19 ++++++++----
>  qemu-img.c                         |  6 ++--
>  qobject/qobject-json.c             | 61 ++++++++++++++++++++++++--------------
>  tests/check-qobject-json.c         | 15 ++++++++--
>  tests/test-json-output-visitor.c   | 17 +++++++++--
>  tests/test-qmp-output-visitor.c    | 17 +++++++++++
>  8 files changed, 107 insertions(+), 38 deletions(-)
>
> diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
> index 414f91b..2ce2758 100644
> --- a/include/qapi/json-output-visitor.h
> +++ b/include/qapi/json-output-visitor.h
> @@ -23,7 +23,12 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
>   *
>   * If @pretty, make the output legible with newlines and indentation;
>   * otherwise the output uses a single line.
> + *
> + * If @strict, attempts to output invalid JSON (strings not properly
> + * encoded in UTF-8, or Infinity or NaN as a number) will be treated
> + * as an error; otherwise, output is best-effort even if not pure
> + * JSON.
>   */
> -Visitor *json_output_visitor_new(bool pretty, char **result);
> +Visitor *json_output_visitor_new(bool pretty, bool strict, char **result);
>
>  #endif
> diff --git a/include/qapi/qmp/qobject-json.h b/include/qapi/qmp/qobject-json.h
> index 28e44b2..30bc753 100644
> --- a/include/qapi/qmp/qobject-json.h
> +++ b/include/qapi/qmp/qobject-json.h
> @@ -31,7 +31,8 @@ QString *qobject_to_json(const QObject *obj, bool pretty);
>   * (ignored for a top-level visit, must be set if part of a
>   * dictionary, should be NULL if part of a list).
>   */
> -void qobject_visit_output(Visitor *v, const char *name, const QObject *obj);
> +void qobject_visit_output(Visitor *v, const char *name, const QObject *obj,
> +                          Error **errp);
>
>  int qstring_append_json_string(QString *qstring, const char *str);
>  int qstring_append_json_number(QString *qstring, double number);
> diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
> index ba647d3..28e95ee 100644
> --- a/qapi/json-output-visitor.c
> +++ b/qapi/json-output-visitor.c
> @@ -13,11 +13,13 @@
>  #include "qapi/visitor-impl.h"
>  #include "qapi/qmp/qstring.h"
>  #include "qapi/qmp/qobject-json.h"
> +#include "qapi/error.h"
>
>  struct JsonOutputVisitor {
>      Visitor visitor;
>      QString *str;
>      bool pretty;
> +    bool strict;
>      bool comma;
>      unsigned int depth;
>      char **result;
> @@ -133,8 +135,10 @@ static void json_output_type_str(Visitor *v, const char *name, char **obj,
>
>      assert(*obj);
>      json_output_name(jov, name);
> -    /* FIXME: report invalid UTF-8 encoding */
> -    qstring_append_json_string(jov->str, *obj);
> +    if (qstring_append_json_string(jov->str, *obj) < 0 && jov->strict) {
> +        error_setg(errp, "string value of '%s' is not valid UTF-8",
> +                   name ? name : "null");
> +    }
>  }
>
>  static void json_output_type_number(Visitor *v, const char *name, double *obj,
> @@ -143,14 +147,16 @@ static void json_output_type_number(Visitor *v, const char *name, double *obj,
>      JsonOutputVisitor *jov = to_jov(v);
>
>      json_output_name(jov, name);
> -    /* FIXME: report Inf/NaN problems */
> -    qstring_append_json_number(jov->str, *obj);
> +    if (qstring_append_json_number(jov->str, *obj) < 0 && jov->strict) {
> +        error_setg(errp, "number value of '%s' is not finite",
> +                   name ? name : "null");
> +    }
>  }
>
>  static void json_output_type_any(Visitor *v, const char *name, QObject **obj,
>                                   Error **errp)
>  {
> -    qobject_visit_output(v, name, *obj);
> +    qobject_visit_output(v, name, *obj, errp);
>  }
>
>  static void json_output_type_null(Visitor *v, const char *name, Error **errp)
> @@ -181,12 +187,13 @@ static void json_output_free(Visitor *v)
>      g_free(jov);
>  }
>
> -Visitor *json_output_visitor_new(bool pretty, char **result)
> +Visitor *json_output_visitor_new(bool pretty, bool strict, char **result)
>  {
>      JsonOutputVisitor *v;
>
>      v = g_malloc0(sizeof(*v));
>      v->pretty = pretty;
> +    v->strict = strict;
>      v->result = result;
>      *result = NULL;
>      v->str = qstring_new();
> diff --git a/qemu-img.c b/qemu-img.c
> index d5dc19b..b0c6dd3 100644
> --- a/qemu-img.c
> +++ b/qemu-img.c
> @@ -480,7 +480,7 @@ fail:
>  static void dump_json_image_check(ImageCheck *check, bool quiet)
>  {
>      char *str;
> -    Visitor *v = json_output_visitor_new(true, &str);
> +    Visitor *v = json_output_visitor_new(true, true, &str);

Switch to strict not documented in the commit message.  Is it a no-op?

More of the same below.

>
>      visit_type_ImageCheck(v, NULL, &check, &error_abort);
>      visit_complete(v, &str);
> @@ -2166,7 +2166,7 @@ static void dump_snapshots(BlockDriverState *bs)
>  static void dump_json_image_info_list(ImageInfoList *list)
>  {
>      char *str;
> -    Visitor *v = json_output_visitor_new(true, &str);
> +    Visitor *v = json_output_visitor_new(true, true, &str);
>
>      visit_type_ImageInfoList(v, NULL, &list, &error_abort);
>      visit_complete(v, &str);
> @@ -2178,7 +2178,7 @@ static void dump_json_image_info_list(ImageInfoList *list)
>  static void dump_json_image_info(ImageInfo *info)
>  {
>      char *str;
> -    Visitor *v = json_output_visitor_new(true, &str);
> +    Visitor *v = json_output_visitor_new(true, true, &str);
>
>      visit_type_ImageInfo(v, NULL, &info, &error_abort);
>      visit_complete(v, &str);
> diff --git a/qobject/qobject-json.c b/qobject/qobject-json.c
> index 05b020a..cf81ab5 100644
> --- a/qobject/qobject-json.c
> +++ b/qobject/qobject-json.c
> @@ -72,62 +72,81 @@ QObject *qobject_from_jsonf(const char *string, ...)
>      return obj;
>  }
>
> +typedef struct ToJson {
> +{
> +    Visitor *v;
> +    Error **errp;
> +} ToJson;
> +
>  static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
>  {
> -    Visitor *v = opaque;
> +    ToJson *s = opaque;
>
> -    qobject_visit_output(v, key, obj);
> +    if (!*s->errp) {
> +        qobject_visit_output(s->v, key, obj, s->errp);
> +    }
>  }
>
>  static void to_json_list_iter(QObject *obj, void *opaque)
>  {
> -    Visitor *v = opaque;
> +    ToJson *s = opaque;
>
> -    qobject_visit_output(v, NULL, obj);
> +    if (!*s->errp) {
> +        qobject_visit_output(s->v, NULL, obj, s->errp);
> +    }
>  }
>
> -void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
> +void qobject_visit_output(Visitor *v, const char *name, const QObject *obj,
> +                          Error **errp)
>  {
> +    Error *err = NULL;
> +    ToJson s = { .v = v, .errp = &err, };
> +
>      switch (qobject_type(obj)) {
>      case QTYPE_QNULL:
> -        visit_type_null(v, name, &error_abort);
> +        visit_type_null(v, name, errp);
>          break;
>      case QTYPE_QINT: {
>          int64_t val = qint_get_int(qobject_to_qint(obj));
> -        visit_type_int64(v, name, &val, &error_abort);
> +        visit_type_int64(v, name, &val, errp);
>          break;
>      }
>      case QTYPE_QSTRING: {
>          const char *str = qstring_get_str(qobject_to_qstring(obj));
> -        /* FIXME: no way inform user if we modified the string to
> -         * avoid encoding errors */
> -        visit_type_str(v, name, (char **)&str, &error_abort);
> +        visit_type_str(v, name, (char **)&str, errp);
>          break;
>      }
>      case QTYPE_QDICT: {
>          QDict *val = qobject_to_qdict(obj);
> -        visit_start_struct(v, name, NULL, 0, &error_abort);
> -        qdict_iter(val, to_json_dict_iter, v);
> -        visit_check_struct(v, &error_abort);
> +        visit_start_struct(v, name, NULL, 0, &err);
> +        if (!err) {
> +            qdict_iter(val, to_json_dict_iter, &s);
> +        }

       for (ent = qdict_first(val); ent; ent = qdict_next(val, ent)) {
           qobject_visit_output(v, ent->key, ent->value, &err);
       }

> +        if (!err) {
> +            visit_check_struct(v, &err);
> +        }
> +        error_propagate(errp, err);
>          visit_end_struct(v, NULL);
>          break;
>      }
>      case QTYPE_QLIST: {
>          QList *val = qobject_to_qlist(obj);
> -        visit_start_list(v, name, NULL, 0, &error_abort);
> -        qlist_iter(val, to_json_list_iter, v);
> +        visit_start_list(v, name, NULL, 0, &err);
> +        if (!err) {
> +            qlist_iter(val, to_json_list_iter, &s);

               QLIST_FOREACH_ENTRY(val, ent) {
                   qobject_visit_output(s->v, NULL, obj, errp);
               }

> +        }
> +        error_propagate(errp, err);
>          visit_end_list(v, NULL);
>          break;
>      }
>      case QTYPE_QFLOAT: {
>          double val = qfloat_get_double(qobject_to_qfloat(obj));
> -        /* FIXME: no way inform user if we generated invalid JSON */
> -        visit_type_number(v, name, &val, NULL);
> +        visit_type_number(v, name, &val, errp);

Just noticed the pre-patch difference NULL here and &error_abort
elsewhere.  I'd use &error_abort here, too [PATCH 26].

>          break;
>      }
>      case QTYPE_QBOOL: {
>          bool val = qbool_get_bool(qobject_to_qbool(obj));
> -        visit_type_bool(v, name, &val, &error_abort);
> +        visit_type_bool(v, name, &val, errp);
>          break;
>      }
>      default:
> @@ -138,9 +157,9 @@ void qobject_visit_output(Visitor *v, const char *name, const QObject *obj)
>  QString *qobject_to_json(const QObject *obj, bool pretty)
>  {
>      char *str;
> -    Visitor *v = json_output_visitor_new(pretty, &str);
> +    Visitor *v = json_output_visitor_new(pretty, false, &str);
>
> -    qobject_visit_output(v, NULL, obj);
> +    qobject_visit_output(v, NULL, obj, &error_abort);
>      visit_complete(v, &str);
>      visit_free(v);
>      return qstring_wrap_str(str);
> @@ -222,8 +241,6 @@ int qstring_append_json_number(QString *qstring, double number)
>      /* FIXME: snprintf() is locale dependent; but JSON requires
>       * numbers to be formatted as if in the C locale. Dependence
>       * on C locale is a pervasive issue in QEMU. */
> -    /* FIXME: This risks printing Inf or NaN, which are not valid
> -     * JSON values. */

I think this one should be dropped in PATCH 17, where the invalid JSON
becomes a documented interface option.

>      /* FIXME: the default precision of 6 for %f often causes
>       * rounding errors; we should be using DBL_DECIMAL_DIG (17),
>       * and only rounding to a shorter number if the result would
[...]

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

* Re: [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function
  2016-06-01 16:03   ` Markus Armbruster
@ 2016-06-03 11:46     ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03 11:46 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
[...]
>> diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
>> index 9391dea..235e8a1 100644
>> --- a/qapi/qapi-dealloc-visitor.c
>> +++ b/qapi/qapi-dealloc-visitor.c
>> @@ -107,17 +107,12 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp)
>>  {
>>  }
>>
>> -Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
>> -{
>> -    return &v->visitor;
>> -}
>> -
>> -void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v)
>> +static void qapi_dealloc_free(Visitor *v)
>>  {
>>      g_free(v);
>
> Uh, shouldn't this be g_free(v, QapiDeallocVisitor, visitor)?  That way,
> we don't assume that visitor is QapiDeallocVisitor's first member.

I mean g_free(container_of(v, QapiDeallocVisitor, visitor), of course.

>
>>  }
>>
>> -QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
>> +Visitor *qapi_dealloc_visitor_new(void)
>>  {
>>      QapiDeallocVisitor *v;
>>
[...]

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

* Re: [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors
  2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
                   ` (30 preceding siblings ...)
  2016-05-19 17:05 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Markus Armbruster
@ 2016-06-03 12:09 ` Markus Armbruster
  2016-06-09 16:16   ` Eric Blake
  31 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03 12:09 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Most patches are ready as is or with minor touch-ups.

I'd probably drop PATCH 01.  The file name collision no longer exists,
and moving files just to improve their names a bit doesn't seem worth
the bother.

There are memory leaks in PATCH 12 and 24 (see review of PATCH 24).

PATCH 13 needs more work than I'm comfortable to do on commit.  Mostly
missing comment updates, but also a crash bug.

On PATCH 21, I have minor interface design doubts.

The only difficult question is what to do about invalid UTF-8 strings
and non-finite numbers.  Patches before PATCH 29 show opinions, but a
decision isn't made until PATCH 29.  Good, because it leaves most of
this series not blocked by the debate on what we should do.

I guess I could take PATCH 02-12 now, and let you respin the rest.  But
perhaps its easier if you respin all of them.

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

* Re: [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor
  2016-06-03  7:39   ` Markus Armbruster
@ 2016-06-03 12:53     ` Eric Blake
  2016-06-03 14:09       ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-06-03 12:53 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/03/2016 01:39 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> We have several places that want to go from qapi to JSON; right now,
>> they have to create an intermediate QObject to do the work.  That
>> also has the drawback that the JSON formatting of a QDict will
>> rearrange keys (according to a deterministic, but unpredictable,
>> hash), when humans have an easier time if dicts are produced in
>> the same order as the qapi type.
>>

>> +struct JsonOutputVisitor {
>> +    Visitor visitor;
>> +    QString *str;
>> +    bool comma;
>> +    unsigned int depth;
>> +    char **result;
>> +};
>> +

>> +static void json_output_complete(Visitor *v, void *result)
>> +{
>> +    JsonOutputVisitor *jov = to_jov(v);
>> +
>> +    assert(!jov->depth);
>> +    assert(qstring_get_length(jov->str));
>> +    assert(jov->result == result);
>> +    *jov->result = qstring_consume_str(jov->str);
>> +    jov->str = NULL;
>> +    jov->result = NULL;
>> +}
> 
> Related: discussion of complete() semantics in review of PATCH 12.
> Non-idempotent semantics save us a copy, like in
> string_output_complete().
> 
>> +
>> +static void json_output_free(Visitor *v)
>> +{
>> +    JsonOutputVisitor *jov = to_jov(v);
>> +
>> +    QDECREF(jov->str);
>> +    g_free(jov);
> 
> I'm afraid this leaks jov->result when the visitor is destroyed without
> calling its complete() method.

Not true. jov->result is owned by the caller, and not something we
allocate locally.  We set jov->result to NULL to make sure complete() is
not called twice, but we are not responsible for freeing it, since we
didn't allocate it.

> 
> string_output_free() appears to have the same leak.

Same non-bug.

> 
>> +}
>> +
>> +Visitor *json_output_visitor_new(char **result)
>> +{
>> +    JsonOutputVisitor *v;
>> +
>> +    v = g_malloc0(sizeof(*v));
>> +    v->result = result;
>> +    *result = NULL;
>> +    v->str = qstring_new();
>> +


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

* Re: [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in JSON output visitor
  2016-06-03  7:56   ` Markus Armbruster
@ 2016-06-03 12:55     ` Eric Blake
  2016-06-03 14:08       ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-06-03 12:55 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/03/2016 01:56 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Similar to pretty printing in the QObject visitor.  The trickiest
>> part is probably that the testsuite now has to honor parameterization
>> on whether pretty printing is enabled.
> 
> Worth mentioning that the pretty-printing matches the one in
> qobject-json.c?

Yes, particularly since we later rely on that fact to rewrite
qobject-json.c on top of the JSON visitor :)


>>  static void visitor_output_setup(TestOutputVisitorData *data,
>> -                                 const void *unused)
>> +                                 const void *arg)
>>  {
>> -    data->ov = json_output_visitor_new(&data->str);
>> +    const bool *pretty = arg;
> 
> Could do bool pretty = *(bool *)arg.  Matter of taste.  Same elsewhere.

Requires a cast.  I like avoiding casts where C lets us do so. But I
also agree that *pretty looks ugly, so maybe:

...(const void *arg)
{
    const bool *data;
    bool pretty = *data;


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

* Re: [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor
  2016-06-02 13:43   ` Markus Armbruster
@ 2016-06-03 14:04     ` Markus Armbruster
  2016-06-09  4:15     ` Eric Blake
  1 sibling, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03 14:04 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Markus Armbruster <armbru@redhat.com> writes:

> Eric Blake <eblake@redhat.com> writes:
[...]
>> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
>> index 279ea8e..c5bdca2 100644
>> --- a/qapi/qapi-visit-core.c
>> +++ b/qapi/qapi-visit-core.c
[...]
>> @@ -44,10 +44,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
>>
>>      if (obj) {
>>          assert(size);
>> -        assert(v->type != VISITOR_OUTPUT || *obj);
>> +        assert(!(v->type & VISITOR_OUTPUT) || *obj);
>>      }
>
> For real walks (obj != NULL):
>
> * Input visitors write *obj, and don't care for the old value.
>
> * Output visitors read *obj, and a struct can't be null.
>
> * The dealloc visitor reads *obj, but null is fine (partially
>   constructed object).
>
> * The clone visitor reads like an output visitor (except at the root)
>   and writes like an input visitor.
>
> Before the patch, we assert "if output visitor, then *obj isn't null".
>
> After the patch, we do the same for the clone visitor.  Correct, except
> at the root.  There, @obj points to qapi_clone()'s @dst, which is
> uninitialized.  I'm afraid this assertion fails if @dst happens to be
> null.

I can observe this failure in make check when I compile with
optimization.

[...]

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

* Re: [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in JSON output visitor
  2016-06-03 12:55     ` Eric Blake
@ 2016-06-03 14:08       ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03 14:08 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/03/2016 01:56 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> Similar to pretty printing in the QObject visitor.  The trickiest
>>> part is probably that the testsuite now has to honor parameterization
>>> on whether pretty printing is enabled.
>> 
>> Worth mentioning that the pretty-printing matches the one in
>> qobject-json.c?
>
> Yes, particularly since we later rely on that fact to rewrite
> qobject-json.c on top of the JSON visitor :)
>
>
>>>  static void visitor_output_setup(TestOutputVisitorData *data,
>>> -                                 const void *unused)
>>> +                                 const void *arg)
>>>  {
>>> -    data->ov = json_output_visitor_new(&data->str);
>>> +    const bool *pretty = arg;
>> 
>> Could do bool pretty = *(bool *)arg.  Matter of taste.  Same elsewhere.
>
> Requires a cast.  I like avoiding casts where C lets us do so. But I
> also agree that *pretty looks ugly, so maybe:
>
> ...(const void *arg)
> {
>     const bool *data;
>     bool pretty = *data;

Fewer casts are generally better, but so are fewer variables.  Pick a
solution you like.  The one you chose for this patch is acceptable.

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

* Re: [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor
  2016-06-03 12:53     ` Eric Blake
@ 2016-06-03 14:09       ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-03 14:09 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Michael Roth

Eric Blake <eblake@redhat.com> writes:

> On 06/03/2016 01:39 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>>> We have several places that want to go from qapi to JSON; right now,
>>> they have to create an intermediate QObject to do the work.  That
>>> also has the drawback that the JSON formatting of a QDict will
>>> rearrange keys (according to a deterministic, but unpredictable,
>>> hash), when humans have an easier time if dicts are produced in
>>> the same order as the qapi type.
>>>
>
>>> +struct JsonOutputVisitor {
>>> +    Visitor visitor;
>>> +    QString *str;
>>> +    bool comma;
>>> +    unsigned int depth;
>>> +    char **result;
>>> +};
>>> +
>
>>> +static void json_output_complete(Visitor *v, void *result)
>>> +{
>>> +    JsonOutputVisitor *jov = to_jov(v);
>>> +
>>> +    assert(!jov->depth);
>>> +    assert(qstring_get_length(jov->str));
>>> +    assert(jov->result == result);
>>> +    *jov->result = qstring_consume_str(jov->str);
>>> +    jov->str = NULL;
>>> +    jov->result = NULL;
>>> +}
>> 
>> Related: discussion of complete() semantics in review of PATCH 12.
>> Non-idempotent semantics save us a copy, like in
>> string_output_complete().
>> 
>>> +
>>> +static void json_output_free(Visitor *v)
>>> +{
>>> +    JsonOutputVisitor *jov = to_jov(v);
>>> +
>>> +    QDECREF(jov->str);
>>> +    g_free(jov);
>> 
>> I'm afraid this leaks jov->result when the visitor is destroyed without
>> calling its complete() method.
>
> Not true. jov->result is owned by the caller, and not something we
> allocate locally.  We set jov->result to NULL to make sure complete() is
> not called twice, but we are not responsible for freeing it, since we
> didn't allocate it.
>
>> 
>> string_output_free() appears to have the same leak.
>
> Same non-bug.

You're right.

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

* Re: [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_*
  2016-06-01 15:36   ` Markus Armbruster
@ 2016-06-07 23:20     ` Eric Blake
  0 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-06-07 23:20 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core,
	Michael S. Tsirkin, Michael Roth, Alexander Graf,
	open list:sPAPR, Max Reitz, Andreas Färber, David Gibson

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

On 06/01/2016 09:36 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Rather than making the dealloc visitor track of stack of pointers
>> remembered during visit_start_* in order to free them during
>> visit_end_*, it's a lot easier to just make all callers pass the
>> same pointer to visit_end_*.  The generated code has access to the
>> same pointer, while all other users are doing virtual walks and
>> can pass NULL.  The dealloc visitor is then greatly simplified.
>>
>> All three visit_end_*() functions intentionally take a void**,
>> even though the visit_start_*() functions differ between void**,
>> GenericList**, and GenericAlternate**.  This is done for several
>> reasons: when doing a virtual walk, passing NULL doesn't care
>> what the type is, but when doing a generated walk, we already
>> have to cast the caller's specific FOO* to call visit_start,
>> while using void** lets us use visit_end without a cast. Also,
>> an upcoming patch will add a clone visitor that wants to use
>> the same implementation for all three visit_end callbacks,
>> which is made easier if all three share the same signature.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
> [...]
>> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
>> index aea90a1..84f32fc 100644
>> --- a/qapi/qmp-input-visitor.c
>> +++ b/qapi/qmp-input-visitor.c
>> @@ -145,7 +145,7 @@ static void qmp_input_check_struct(Visitor *v, Error **errp)
>>      }
>>  }
>>
>> -static void qmp_input_pop(Visitor *v)
>> +static void qmp_input_pop(Visitor *v, void **obj)
>>  {
>>      QmpInputVisitor *qiv = to_qiv(v);
>>      StackObject *tos = &qiv->stack[qiv->nb_stack - 1];
> 
> You could assert @obj matches tos->obj.  Same for the other visitors
> that still need a stack.  Adding a stack to the ones that don't just for
> the assertion seems excessive, though.

At this point, only the QMP visitors track a stack (the OptsVisitor does
not, and we just got rid of the dealloc visitor stack); but since the
string visitors only support a top-level visit with no struct or nested
list, those can also support an assert. That makes 4 of the 6 visitors
at this stage in the series; and only 4/8 when the clone and json
visitors are added.  I'll go ahead and add it in, though.

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

* Re: [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor
  2016-06-02 13:43   ` Markus Armbruster
  2016-06-03 14:04     ` Markus Armbruster
@ 2016-06-09  4:15     ` Eric Blake
  1 sibling, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-06-09  4:15 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Michael Roth

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

On 06/02/2016 07:43 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> We have a couple places in the code base that want to deep-clone
>> one QAPI object into another, and they were resorting to serializing
>> the struct out to QObject then reparsing it.  A much more efficient
>> version can be done by adding a new clone visitor.
>>

> [...]
>     * 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.
> 
> What about the clone visitor?

Probably safest to document it as undefined on incomplete objects.

>    /*
>     * 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.
> 
> What about the clone visitor?

Yes, clone visitors also use size.

> 
>     *
>     * @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.
> 
> What about the clone visitor?

Never sets an error (ie. it can't fail on a complete source object, if
you don't include abort-due-to-OOM scenarios), so I'm not sure I need to
word anything differently here.

>     * Start visiting a list.
>     *
>     * @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.
> 
> What about the clone visitor?
> 
>     *
>     * @errp obeys typical error usage, and reports failures such as a
>     * member @name is not present, or present but not a list.  On error,
>     * input visitors set *@list to NULL.
> 
> What about the clone visitor?

Same as for start_struct.

>    /*
>     * 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.
> 
> I guess this is correct for the clone visitor.

Clone visitor leaves it alone (it is reading *@present on the dest,
which was already set earlier during the g_memdup() of visit_start_*).

>    /*
>     * Visit an enum value.
>     *
>     * @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.
> 
> I guess this is correct for the clone visitor.

It's a bit of a stretch, but "use *@obj" can certainly mean "do nothing
with it, because it is a scalar that was already set earlier during the
g_memdup() of visit_start_*".  So yes, the clone visitor wants
visit_type_enum() to be a no-op.


> 
>    /*
>     * Check if visitor is an input visitor.
> 
> Does the clone visitor count as input visitor here?  Should it?

No, and probably no.  A clone visitor never sets errp, and therefore
there is no reason to clean up after a failed clone; and our current use
of visit_is_input() is only for cleaning up after failures in an input
visitor.

> 
>     */
>    bool visit_is_input(Visitor *v);
> 
>    /*** 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.
> 
> I guess this is correct for the clone visitor.

Again correct - the clone visitor doesn't set anything at this point,
because the integer was already copied earlier during the g_memdup() of
visit_start_*().


>    /*
>     * 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 "".
> 
> I guess this is correct for the clone visitor.

The clone visitor could morph NULL into "" (I didn't code it that way,
though).  Here, the clone visitor DOES set *@obj, in order to dedupe the
pointer from the source object, so maybe a third sentence is needed?


>    /*
>     * 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.
> 
> Fine, as the clone visitor doesn't support any.

It could, if we use the JSON output visitor code later in the series to
create a QObject deep-cloner, but I'd rather not do it unless we find an
actual need (keeping 'any' out of clone does simplify the number of
corner cases to think about).

>> +++ b/qapi/qapi-visit-core.c
> 
> As we'll see further down, @obj points into the clone, except at the
> root, where it points to qapi_clone()'s local variable @dst.  A
> pointer-valued *@obj still points into the source.
> 
> Now let's go through the v->type checks real slow.
> 

>> @@ -44,10 +44,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
>>
>>      if (obj) {
>>          assert(size);
>> -        assert(v->type != VISITOR_OUTPUT || *obj);
>> +        assert(!(v->type & VISITOR_OUTPUT) || *obj);
>>      }
> 
> For real walks (obj != NULL):
> 
> * Input visitors write *obj, and don't care for the old value.
> 
> * Output visitors read *obj, and a struct can't be null.
> 
> * The dealloc visitor reads *obj, but null is fine (partially
>   constructed object).
> 
> * The clone visitor reads like an output visitor (except at the root)
>   and writes like an input visitor.
> 
> Before the patch, we assert "if output visitor, then *obj isn't null".
> 
> After the patch, we do the same for the clone visitor.  Correct, except
> at the root.  There, @obj points to qapi_clone()'s @dst, which is
> uninitialized.  I'm afraid this assertion fails if @dst happens to be
> null.
> 
> Can we fix this by removing the "except at the root" special case?
> Change qapi_clone to initialize dst = src, drop QapiCloneVisitor member
> @root and qapi_clone_visitor_new() parameter @src.

Cool idea!  And it avoids the crash (I was indeed compiling without
optimization, and getting lucky that the uninit value wasn't crashing my
tests; wonder why valgrind wasn't flagging it).


> [...]
>    bool visit_is_input(Visitor *v)
>    {
>        return v->type == VISITOR_INPUT;
>    }
> 
> This answers my question "Does the clone visitor count as input visitor
> here?"  Remaining question: "Should it?"

I'm still not convinced, again on the grounds that this is used for
cleanup after a failed visit, but clone visits don't fail.

> 
>> @@ -252,9 +252,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>>      assert(obj);
>>      /* TODO: Fix callers to not pass NULL when they mean "", so that we
>>       * can enable:
>> -    assert(v->type != VISITOR_OUTPUT || *obj);
>> +    assert(!(v->type & VISITOR_OUTPUT) || *obj);
>>       */
>>      v->type_str(v, name, obj, &err);
>> +    /* Likewise, use of NULL means we can't do (v->type & VISITOR_INPUT) */
>>      if (v->type == VISITOR_INPUT) {
>>          assert(!err != !*obj);
>>      }
> 
> If your head doesn't hurt by know, you either wrote this, or you're not
> reading closely :)

And there's my idea of making the clone visitor auto-magically clone
NULL into "", at which point the conditions in the assertions would change.

> 
> If the TODOs were already addressed, we'd again get the same analysis as
> for visit_start_struct(), except for the arguments about the root, which
> don't apply here, because the clone visitor doesn't accept scalar roots.
> 
> In the current state, the analysis needs to be modified as follows.
> 
> First assertion:
> 
> Before the patch, we'd like to assert "if output or clone visitor, then
> *obj isn't null".  We can't as long as we need to treat null as the
> empty string.
> 
> After the patch, the situation is the same for the clone visitor.  Okay.
> 
> Second assertion:
> 
> Before the patch, we assert "input visitor must either fail or create
> *obj for a real walk."  The TODO doesn't apply; we create "", not null.
> 
> After the patch, we'd like to assert the same for the clone visitor, but
> we can't: the clone of null is null.  Okay.
> 
>> @@ -273,9 +274,9 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
>>      Error *err = NULL;
>>
>>      assert(obj);
>> -    assert(v->type != VISITOR_OUTPUT || *obj);
>> +    assert(!(v->type & VISITOR_OUTPUT) || *obj);
>>      v->type_any(v, name, obj, &err);
>> -    if (v->type == VISITOR_INPUT) {
>> +    if (v->type & VISITOR_INPUT) {
>>          assert(!err != !*obj);
>>      }
>>      error_propagate(errp, err);
> 
> v->type_any() will crash for the clone visitor, so these changes aren't.
> necessary.  If you want them to future-proof the code, I need to
> convince myself the changes make sense, similar to what I did for the
> other ones in this file.

Okay, I can leave this hunk out for now.

> 
>> @@ -342,4 +343,5 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
>>      } else if (v->type == VISITOR_OUTPUT) {
>>          output_type_enum(v, name, obj, strings, errp);
>>      }
>> +    /* dealloc and clone visitors have nothing to do */
>>  }
> 
> I'm upgrade my verdict from "subtle" to "scarily subtle" %-}
> 

Any comments I can add to make it more obvious to the next reader?

>> +
>> +static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj,
>> +                                    size_t size, Error **errp)
>> +{
>> +    QapiCloneVisitor *qcv = to_qcv(v);
>> +
>> +    if (!obj) {
>> +        /* Only possible when visiting an alternate's object
>> +         * branch. Nothing to do here, since the earlier
>> +         * visit_start_alternate() already copied memory. */
> 
> Should visitor-impl.h explain how method start_struct() is used with
> alternates?  I once again forgot how this works...  Hmm, you explained
> it to me during review of v3.
> 
> Despite there's "nothing to do here", you found something to do:
> 
>> +        assert(qcv->depth);

That's really only asserting that the clone itself is a real visit; we
don't allow cloning on a virtual visit, even though the real visit of an
alternate also involves the virtual visit of an object, if the
alternate's object branch is selected.


> [Skipping the tests for now to get this review out today...]

Did you want to review the tests in any further detail?

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-06-03  9:02       ` Markus Armbruster
@ 2016-06-09 16:07         ` Eric Blake
  2016-06-13  8:22           ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-06-09 16:07 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Luiz Capitulino

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

On 06/03/2016 03:02 AM, Markus Armbruster wrote:

>>> Suggest:
>>>
>>>  * Return 0 if the number is finite, as required by RFC 7159, else -1.
>>>
>>> The return value makes some sense only for symmetry with
>>> qstring_append_json_string().  Without that, I'd ask you to keep this
>>> function simple.  Callers could just as easily test isfinite()
>>> themselves.
>>
>> I'm actually thinking of modifying this, given the recent thread
>> pointing out that libvirt chokes hard on JSON extensions:
>>
>> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg04398.html
>>
>> That is, for symmetry with qstring_append_json_string(), I'm thinking of
>> changing NaN to 0 and Inf to DBL_MAX, and always outputting a finite
>> value, in addition to returning -1 to inform the caller that a
>> substitution was made, so that the output is always strict JSON.
> 
> Mapping infinities to DBL_MIN and DBL_MAX is debatable, but mapping NaN
> to zero is outright wrong.

How about this alternative:

Finite values remain numbers:
"number":1

But non-finite values are output as strings, so that our output is
always valid JSON - the recipient may not be expecting a string in place
of a number, but at least should be able to parse the output rather than
choking hard.
"number":"nan"

The return value -1 then indicates that a stringized replacement was
used, so that any later patch can use a strict flag on whether to allow
the replacement output or assert.

> 
> If we decide QMP should stick to JSON here and avoid non-finite numbers,
> we need to treat an attempt to emit a non-finite number as a bug:
> assert(isfinite(...)).  Making sure nothing ever attempts to emit such
> numbers will be tedious.
> 
> If we decide QMP should remain as it is, we need to document non-finite
> numbers among its JSON extensions.  We should also fix our QMP parsers
> to accept non-finite numbers then.  Including the one in libvirt.
> Attempts to emit non-finite numbers then *may* be bugs.  Really no
> different than finite numbers outside their intended range, such as a
> negative size.  Catching these bugs is of course also tedious.  The
> difference is that they manifest in QMP as semantic instead of lexical
> errors.  Lexical errors are the worst to handle gracefully.

I may still try to tackle fixing the QMP parser to accept NaN and
infinity on input (since it's hand-written, we at least have control
over that) - it will certainly be easier than getting libvirt to parse
non-finite numbers (libvirt uses libyajl, and my emails to the yajl
mailing list have gone unanswered, making me think the project is not
very vibrant and thus not very patchable).  But with my proposal of
producing a stringized non-finite value, we at least convert lexical
into semantic errors, which I agree with your assessment is a nicer way
of dealing with it.

Of course, a policy change of outputting stringized non-finite numbers
should be separate from refactoring patches that just move functions around.

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

* Re: [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors
  2016-06-03 12:09 ` Markus Armbruster
@ 2016-06-09 16:16   ` Eric Blake
  2016-06-13  8:26     ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-06-09 16:16 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

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

On 06/03/2016 06:09 AM, Markus Armbruster wrote:
> Most patches are ready as is or with minor touch-ups.
> 
> I'd probably drop PATCH 01.  The file name collision no longer exists,
> and moving files just to improve their names a bit doesn't seem worth
> the bother.

It's a bit more churn to the rest of the series, but I can live with it.
It's also more related to the JSON visitor than the clone visitor, so
even if I do want to keep it, it makes more sense to keep it with the
second half of the series.

> 
> There are memory leaks in PATCH 12 and 24 (see review of PATCH 24).
> 
> PATCH 13 needs more work than I'm comfortable to do on commit.  Mostly
> missing comment updates, but also a crash bug.

I've got that fix ready to post.

> 
> On PATCH 21, I have minor interface design doubts.
> 
> The only difficult question is what to do about invalid UTF-8 strings
> and non-finite numbers.  Patches before PATCH 29 show opinions, but a
> decision isn't made until PATCH 29.  Good, because it leaves most of
> this series not blocked by the debate on what we should do.
> 
> I guess I could take PATCH 02-12 now, and let you respin the rest.  But
> perhaps its easier if you respin all of them.

I'm posting v5 of 2-15 now, then focusing on your comments on the JSON
visitor for a later posting.

I also think my subset F series (adding a 'box' parameter) is relatively
orthogonal, if you want to start reviewing that:
https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg03569.html

I'd still LOVE to get blockdev-add working for ALL block devices before
2.7 soft freeze (there's patches for NBD that I need to review, and I
know the gluster folks were waiting on qapi patches to be able to use a
SocketAddress union as the basis for the gluster branch of the
blockdev-add union - and my qapi patches are almost all the way there to
permitting it).

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


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

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

* Re: [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str()
  2016-06-02 15:21   ` Markus Armbruster
@ 2016-06-09 16:31     ` Eric Blake
  0 siblings, 0 replies; 73+ messages in thread
From: Eric Blake @ 2016-06-09 16:31 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Kevin Wolf, open list:Block layer core,
	Chrysostomos Nanakos, Jeff Cody, Luiz Capitulino, Max Reitz

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

On 06/02/2016 09:21 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Several spots in the code malloc a string, then wrap it in a
>> QString, which has to duplicate the input.  Adding a new
>> constructor with transfer semantics makes this pattern more
>> efficient, comparable to the just-added transfer semantics to
>> go from QString back to raw string.  Use the new
>> qstring_wrap_str() where it makes sense.
>>
>> The new test still passes under valgrind.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>

>> +++ b/qobject/qstring.c
>> @@ -66,6 +66,31 @@ QString *qstring_from_str(const char *str)
>>      return qstring_from_substr(str, 0, strlen(str) - 1);
>>  }
>>
>> +/**
>> + * qstring_wrap_str(): Create a new QString around a malloc'd C string
>> + *
>> + * Returns a strong reference, and caller must not use @str any more.
>> + * @str may be NULL, in which case the QString will be "".
> 
> I'm not fond of conflating null pointers and "" (see also the trouble
> with visit_type_str()).  For what it's worth, qstring_from_str(NULL)
> crashes.  Can we reject null?

Probably. I'll add an assert, and update the interface to match if
things still pass with my usage.

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-06-09 16:07         ` Eric Blake
@ 2016-06-13  8:22           ` Markus Armbruster
  2016-06-13 12:34             ` Eric Blake
  0 siblings, 1 reply; 73+ messages in thread
From: Markus Armbruster @ 2016-06-13  8:22 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> On 06/03/2016 03:02 AM, Markus Armbruster wrote:
>
>>>> Suggest:
>>>>
>>>>  * Return 0 if the number is finite, as required by RFC 7159, else -1.
>>>>
>>>> The return value makes some sense only for symmetry with
>>>> qstring_append_json_string().  Without that, I'd ask you to keep this
>>>> function simple.  Callers could just as easily test isfinite()
>>>> themselves.
>>>
>>> I'm actually thinking of modifying this, given the recent thread
>>> pointing out that libvirt chokes hard on JSON extensions:
>>>
>>> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg04398.html
>>>
>>> That is, for symmetry with qstring_append_json_string(), I'm thinking of
>>> changing NaN to 0 and Inf to DBL_MAX, and always outputting a finite
>>> value, in addition to returning -1 to inform the caller that a
>>> substitution was made, so that the output is always strict JSON.
>> 
>> Mapping infinities to DBL_MIN and DBL_MAX is debatable, but mapping NaN
>> to zero is outright wrong.
>
> How about this alternative:
>
> Finite values remain numbers:
> "number":1
>
> But non-finite values are output as strings, so that our output is
> always valid JSON - the recipient may not be expecting a string in place
> of a number, but at least should be able to parse the output rather than
> choking hard.
> "number":"nan"
>
> The return value -1 then indicates that a stringized replacement was
> used, so that any later patch can use a strict flag on whether to allow
> the replacement output or assert.

I dislike this a lot less than mapping non-finite numbers to finite
ones.

I still dislike it, because it defeats fitting a schema to QMP: instead
of the true JSON type "number", we'd need the sum type of "number" and
"string", which this really isn't: only a few special strings are valid,
and they're not actually strings.  If the schema language can do sum
types, we'd even be stuck with their common super-type.

The most practical solution isn't always a likable one, though.

>> If we decide QMP should stick to JSON here and avoid non-finite numbers,
>> we need to treat an attempt to emit a non-finite number as a bug:
>> assert(isfinite(...)).  Making sure nothing ever attempts to emit such
>> numbers will be tedious.
>> 
>> If we decide QMP should remain as it is, we need to document non-finite
>> numbers among its JSON extensions.  We should also fix our QMP parsers
>> to accept non-finite numbers then.  Including the one in libvirt.
>> Attempts to emit non-finite numbers then *may* be bugs.  Really no
>> different than finite numbers outside their intended range, such as a
>> negative size.  Catching these bugs is of course also tedious.  The
>> difference is that they manifest in QMP as semantic instead of lexical
>> errors.  Lexical errors are the worst to handle gracefully.
>
> I may still try to tackle fixing the QMP parser to accept NaN and
> infinity on input (since it's hand-written, we at least have control
> over that)

Making json-lexer.c recognize infinities and NaNs in strtod() syntax
shouldn't be hard.  I'd omit nan(n-char-sequence-opt), because its
semantics are implementation defined.  I'd also omit all spellings other
than inf and nan.  That leaves inf, +inf, -inf, nan, +nan, -nan.

>            - it will certainly be easier than getting libvirt to parse
> non-finite numbers (libvirt uses libyajl, and my emails to the yajl
> mailing list have gone unanswered, making me think the project is not
> very vibrant and thus not very patchable).

Nobody likes to carry downstream patches, but an unresponsive upstream
may leave you no choice.

>                                             But with my proposal of
> producing a stringized non-finite value, we at least convert lexical
> into semantic errors, which I agree with your assessment is a nicer way
> of dealing with it.
>
> Of course, a policy change of outputting stringized non-finite numbers
> should be separate from refactoring patches that just move functions around.

Yes.

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

* Re: [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors
  2016-06-09 16:16   ` Eric Blake
@ 2016-06-13  8:26     ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-13  8:26 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> On 06/03/2016 06:09 AM, Markus Armbruster wrote:
>> Most patches are ready as is or with minor touch-ups.
>> 
>> I'd probably drop PATCH 01.  The file name collision no longer exists,
>> and moving files just to improve their names a bit doesn't seem worth
>> the bother.
>
> It's a bit more churn to the rest of the series, but I can live with it.
> It's also more related to the JSON visitor than the clone visitor, so
> even if I do want to keep it, it makes more sense to keep it with the
> second half of the series.

Thanks.

>> There are memory leaks in PATCH 12 and 24 (see review of PATCH 24).
>> 
>> PATCH 13 needs more work than I'm comfortable to do on commit.  Mostly
>> missing comment updates, but also a crash bug.
>
> I've got that fix ready to post.
>
>> 
>> On PATCH 21, I have minor interface design doubts.
>> 
>> The only difficult question is what to do about invalid UTF-8 strings
>> and non-finite numbers.  Patches before PATCH 29 show opinions, but a
>> decision isn't made until PATCH 29.  Good, because it leaves most of
>> this series not blocked by the debate on what we should do.
>> 
>> I guess I could take PATCH 02-12 now, and let you respin the rest.  But
>> perhaps its easier if you respin all of them.
>
> I'm posting v5 of 2-15 now, then focusing on your comments on the JSON
> visitor for a later posting.

Posted as "[PATCH v5 00/15] Add clone visitor".  Will review as soon as
I can.

> I also think my subset F series (adding a 'box' parameter) is relatively
> orthogonal, if you want to start reviewing that:
> https://lists.gnu.org/archive/html/qemu-devel/2016-05/msg03569.html
>
> I'd still LOVE to get blockdev-add working for ALL block devices before
> 2.7 soft freeze (there's patches for NBD that I need to review, and I
> know the gluster folks were waiting on qapi patches to be able to use a
> SocketAddress union as the basis for the gluster branch of the
> blockdev-add union - and my qapi patches are almost all the way there to
> permitting it).

I'd very much like to finish this huge task, too.  Let's try.

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-06-13  8:22           ` Markus Armbruster
@ 2016-06-13 12:34             ` Eric Blake
  2016-06-13 14:41               ` Markus Armbruster
  0 siblings, 1 reply; 73+ messages in thread
From: Eric Blake @ 2016-06-13 12:34 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel, Luiz Capitulino

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

On 06/13/2016 02:22 AM, Markus Armbruster wrote:

>> I may still try to tackle fixing the QMP parser to accept NaN and
>> infinity on input (since it's hand-written, we at least have control
>> over that)
> 
> Making json-lexer.c recognize infinities and NaNs in strtod() syntax
> shouldn't be hard.  I'd omit nan(n-char-sequence-opt), because its
> semantics are implementation defined.  I'd also omit all spellings other
> than inf and nan.  That leaves inf, +inf, -inf, nan, +nan, -nan.

+inf and +nan weren't worth it (JSON doesn't accept +0 either), but
'-inf' and '-nan' were easy.  In fact, '-infinity' was easy too - see
the posted patches:
https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg02652.html
and you are right that it was not worth 'nan(n-char-seq)'.

> 
>>            - it will certainly be easier than getting libvirt to parse
>> non-finite numbers (libvirt uses libyajl, and my emails to the yajl
>> mailing list have gone unanswered, making me think the project is not
>> very vibrant and thus not very patchable).
> 
> Nobody likes to carry downstream patches, but an unresponsive upstream
> may leave you no choice.

Another (possibly-ugly) option that I thought of over the weekend: We
have 'qmp_capabilities' for handshakes between client and server.  The
server could advertise a new capability 'nonfinite' in its initial
greeting, and if the client replies with the same capability, then the
server knows that the client is prepared to accept bareword non-finite
numbers.  A client that doesn't see the server advertise anything has no
guarantees, and a server that knows the capability but doesn't see the
client request the capability should avoid sending bareword non-finite.

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

* Re: [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting
  2016-06-13 12:34             ` Eric Blake
@ 2016-06-13 14:41               ` Markus Armbruster
  0 siblings, 0 replies; 73+ messages in thread
From: Markus Armbruster @ 2016-06-13 14:41 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel, Luiz Capitulino

Eric Blake <eblake@redhat.com> writes:

> On 06/13/2016 02:22 AM, Markus Armbruster wrote:
>
>>> I may still try to tackle fixing the QMP parser to accept NaN and
>>> infinity on input (since it's hand-written, we at least have control
>>> over that)
>> 
>> Making json-lexer.c recognize infinities and NaNs in strtod() syntax
>> shouldn't be hard.  I'd omit nan(n-char-sequence-opt), because its
>> semantics are implementation defined.  I'd also omit all spellings other
>> than inf and nan.  That leaves inf, +inf, -inf, nan, +nan, -nan.
>
> +inf and +nan weren't worth it (JSON doesn't accept +0 either), but
> '-inf' and '-nan' were easy.  In fact, '-infinity' was easy too - see
> the posted patches:
> https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg02652.html
> and you are right that it was not worth 'nan(n-char-seq)'.
>
>> 
>>>            - it will certainly be easier than getting libvirt to parse
>>> non-finite numbers (libvirt uses libyajl, and my emails to the yajl
>>> mailing list have gone unanswered, making me think the project is not
>>> very vibrant and thus not very patchable).
>> 
>> Nobody likes to carry downstream patches, but an unresponsive upstream
>> may leave you no choice.
>
> Another (possibly-ugly) option that I thought of over the weekend: We
> have 'qmp_capabilities' for handshakes between client and server.  The
> server could advertise a new capability 'nonfinite' in its initial
> greeting, and if the client replies with the same capability, then the
> server knows that the client is prepared to accept bareword non-finite
> numbers.  A client that doesn't see the server advertise anything has no
> guarantees, and a server that knows the capability but doesn't see the
> client request the capability should avoid sending bareword non-finite.

The capability negotiation mechanism exists to support things like that.
However, the problem what to do when the command reply contains
non-finite numbers remains.  A capability can at best hide whatever ugly
solution we have for that, and perhaps help us get rid of it in an
orderly fashion eventually, by refusing to talk to clients that don't
accept the capability.

Moreover, we're already sending non-finite numbers!  Changing QMP to
send something else unless the client opts into non-finite numbers via
capability "nonfinite" is an incompatible change.  We could weasel out
of that by declaring our sending of them a bug, then turn around and
declare it a feature when capability "nonfinite" is accepted.  Meh.

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

end of thread, other threads:[~2016-06-13 14:41 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-19  4:40 [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
2016-05-19  4:40 ` [PATCH v4 01/28] qapi: Rename (one) qjson.h to qobject-json.h Eric Blake
2016-05-19  4:40   ` [Qemu-devel] " Eric Blake
2016-06-01 15:09   ` Markus Armbruster
2016-06-01 15:09     ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 02/28] qapi: Improve use of qmp/types.h Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 03/28] qemu-img: Don't leak errors when outputting JSON Eric Blake
2016-06-01 15:25   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 04/28] qapi: Add parameter to visit_end_* Eric Blake
2016-06-01 15:36   ` Markus Armbruster
2016-06-07 23:20     ` Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 05/28] qapi: Add new visit_free() function Eric Blake
2016-06-01 16:03   ` Markus Armbruster
2016-06-03 11:46     ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 06/28] opts-visitor: Favor " Eric Blake
2016-06-01 16:06   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 07/28] string-input-visitor: " Eric Blake
2016-06-01 16:13   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 08/28] qmp-input-visitor: " Eric Blake
2016-06-01 16:19   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 09/28] string-output-visitor: " Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 10/28] qmp-output-visitor: " Eric Blake
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 11/28] tests: Factor out common code in qapi output tests Eric Blake
2016-06-01 16:33   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 12/28] qapi: Add new visit_complete() function Eric Blake
2016-06-01 17:02   ` Markus Armbruster
2016-05-19  4:40 ` [Qemu-devel] [PATCH v4 13/28] qapi: Add new clone visitor Eric Blake
2016-06-02 13:43   ` Markus Armbruster
2016-06-03 14:04     ` Markus Armbruster
2016-06-09  4:15     ` Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 14/28] sockets: Use new QAPI cloning Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 15/28] replay: " Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 16/28] qapi: Factor out JSON string escaping Eric Blake
2016-06-02 14:53   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 17/28] qapi: Factor out JSON number formatting Eric Blake
2016-06-02 15:02   ` Markus Armbruster
2016-06-02 15:06     ` Eric Blake
2016-06-03  9:02       ` Markus Armbruster
2016-06-09 16:07         ` Eric Blake
2016-06-13  8:22           ` Markus Armbruster
2016-06-13 12:34             ` Eric Blake
2016-06-13 14:41               ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 18/28] qapi: Add qstring_append_printf() Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 19/28] qapi: Use qstring_append_chr() where appropriate Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 20/28] qstring: Add qstring_consume_str() Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 21/28] qstring: Add qstring_wrap_str() Eric Blake
2016-06-02 15:21   ` Markus Armbruster
2016-06-09 16:31     ` Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 22/28] qobject: Consolidate qobject_to_json() calls Eric Blake
2016-06-02 15:32   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 23/28] tests: Test qobject_to_json() pretty formatting Eric Blake
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 24/28] qapi: Add JSON output visitor Eric Blake
2016-06-03  7:39   ` Markus Armbruster
2016-06-03 12:53     ` Eric Blake
2016-06-03 14:09       ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 25/28] qapi: Support pretty printing in " Eric Blake
2016-06-03  7:56   ` Markus Armbruster
2016-06-03 12:55     ` Eric Blake
2016-06-03 14:08       ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 26/28] qobject: Implement qobject_to_json() atop JSON visitor Eric Blake
2016-06-03  8:25   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 27/28] qapi: Add 'any' support to JSON output Eric Blake
2016-06-03  8:29   ` Markus Armbruster
2016-05-19  4:41 ` [Qemu-devel] [PATCH v4 28/28] qemu-img: Use new JSON output formatter Eric Blake
2016-05-19 14:58 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Eric Blake
2016-05-19 16:52 ` [Qemu-devel] [PATCH v4 29/28] qapi: Add strict mode to JSON output visitor Eric Blake
2016-05-19 20:18   ` Eric Blake
2016-06-03  8:36     ` Markus Armbruster
2016-06-03  9:21   ` Markus Armbruster
2016-05-19 17:05 ` [Qemu-devel] [PATCH v4 00/28] Add qapi-to-JSON and clone visitors Markus Armbruster
2016-06-03 12:09 ` Markus Armbruster
2016-06-09 16:16   ` Eric Blake
2016-06-13  8:26     ` Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.