[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 15/18] qapi: Add new clone visitor
From: |
Eric Blake |
Subject: |
[Qemu-devel] [PATCH v3 15/18] qapi: Add new clone visitor |
Date: |
Thu, 28 Apr 2016 22:23:36 -0600 |
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.
Note that we can only clone objects (including alternates) and lists,
not built-ins. This is because of things like visit_type_uint8: our
visitor only implements a 64-bit callback, so we have no indication
what size int to read from the source, and cannot blindly assume that
it is safe to read a 64-bit int. As long as a built-in is not the
root of the visit, scalars copy over just fine by virtue of a
g_memdup() each time we push another struct onto the stack.
As such, I tried to document that the clone visitor is for direct
use only by generated code; other code should stick to wrappers.
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 <address@hidden>
---
v3: new patch
---
include/qapi/visitor.h | 27 ++---
include/qapi/visitor-impl.h | 1 +
scripts/qapi-types.py | 42 ++++++++
include/qapi/clone-visitor.h | 28 +++++
qapi/qapi-visit-core.c | 1 +
qapi/qapi-clone-visitor.c | 166 ++++++++++++++++++++++++++++++
tests/test-clone-visitor.c | 239 +++++++++++++++++++++++++++++++++++++++++++
docs/qapi-code-gen.txt | 38 +++++++
qapi/Makefile.objs | 2 +-
tests/.gitignore | 1 +
tests/Makefile | 5 +-
11 files changed, 536 insertions(+), 14 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 e8a4403..4c122cc 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -24,14 +24,15 @@
* 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, string, and
* JSON) 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
+ * 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
+ * clone, string and QemuOpts visitors have some implementation
* limitations; see the documentation for each visitor for more
* details on what it supports. Also, see visitor-impl.h for the
* callback contracts implemented by each visitor, and
@@ -85,16 +86,18 @@
* struct.
*
* Additionally, in qapi-types.h, all QAPI pointer types (structs,
- * unions, alternates, and lists) have a generated function compatible
- * with:
+ * unions, alternates, and lists) have two generated functions
+ * compatible with:
*
+ * FOO *qapi_FOO_clone(const FOO *src);
* 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 the former does a deep clone, and the latter behaves like
+ * free() in that @obj may be NULL. Because of these functions, the
+ * clone and dealloc visitor are seldom used directly outside of
+ * generated code. QAPI types can also inherit from a base class;
+ * when this happens, a function is generated for easily going from
+ * the derived type to the base type:
*
* BASE *qapi_CHILD_base(CHILD *obj);
*
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 145afd0..a5a2dd0 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -35,6 +35,7 @@ typedef enum VisitorType {
VISITOR_INPUT,
VISITOR_OUTPUT,
VISITOR_DEALLOC,
+ VISITOR_CLONE,
} VisitorType;
struct Visitor
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 437cf6c..c5ac493 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -116,6 +116,38 @@ static inline %(base)s *qapi_%(c_name)s_base(const
%(c_name)s *obj)
c_name=c_name(name), base=base.c_name())
+def gen_type_clone_decl(name):
+ return mcgen('''
+
+%(c_name)s *qapi_%(c_name)s_clone(const %(c_name)s *src);
+''',
+ c_name=c_name(name))
+
+
+def gen_type_clone(name):
+ ret = mcgen('''
+
+%(c_name)s *qapi_%(c_name)s_clone(const %(c_name)s *src)
+{
+ QapiCloneVisitor *qcv;
+ Visitor *v;
+ %(c_name)s *dst;
+
+ if (!src) {
+ return NULL;
+ }
+
+ qcv = qapi_clone_visitor_new(src);
+ v = qapi_clone_get_visitor(qcv);
+ visit_type_%(c_name)s(v, NULL, &dst, &error_abort);
+ qapi_clone_visitor_cleanup(qcv);
+ return dst;
+}
+''',
+ c_name=c_name(name))
+ return ret
+
+
def gen_variants(variants):
ret = mcgen('''
union { /* union tag is @%(c_name)s */
@@ -212,12 +244,16 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
if isinstance(element_type, QAPISchemaBuiltinType):
self._btin += gen_fwd_object_or_array(name)
self._btin += gen_array(name, element_type)
+ self._btin += gen_type_clone_decl(name)
self._btin += gen_type_cleanup_decl(name)
if do_builtins:
+ self.defn += gen_type_clone(name)
self.defn += gen_type_cleanup(name)
else:
self._fwdecl += gen_fwd_object_or_array(name)
self.decl += gen_array(name, element_type)
+ self.decl += gen_type_clone_decl(name)
+ self.defn += gen_type_clone(name)
self._gen_type_cleanup(name)
def visit_object_type(self, name, info, base, members, variants):
@@ -232,11 +268,15 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
# directly use rather than repeat type.is_implicit()?
if not name.startswith('q_'):
# implicit types won't be directly allocated/freed
+ self.decl += gen_type_clone_decl(name)
+ self.defn += gen_type_clone(name)
self._gen_type_cleanup(name)
def visit_alternate_type(self, name, info, variants):
self._fwdecl += gen_fwd_object_or_array(name)
self.decl += gen_object(name, None, [variants.tag_member], variants)
+ self.decl += gen_type_clone_decl(name)
+ self.defn += gen_type_clone(name)
self._gen_type_cleanup(name)
# If you link code generated from multiple schemata, you want only one
@@ -288,7 +328,9 @@ h_comment = '''
fdef.write(mcgen('''
#include "qemu/osdep.h"
+#include "qapi/clone-visitor.h"
#include "qapi/dealloc-visitor.h"
+#include "qapi/error.h"
#include "%(prefix)sqapi-types.h"
#include "%(prefix)sqapi-visit.h"
''',
diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h
new file mode 100644
index 0000000..8da5d0f
--- /dev/null
+++ b/include/qapi/clone-visitor.h
@@ -0,0 +1,28 @@
+/*
+ * Clone Visitor
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QAPI_CLONE_VISITOR_H
+#define QAPI_CLONE_VISITOR_H
+
+#include "qapi/visitor.h"
+
+typedef struct QapiCloneVisitor QapiCloneVisitor;
+
+/* The clone visitor is for use only by generated qapi_FOO_clone()
+ * functions; it requires that the root visit occur on an object,
+ * list, or alternate, and is directly not usable on built-in QAPI
+ * types.
+ */
+QapiCloneVisitor *qapi_clone_visitor_new(const void *src);
+void qapi_clone_visitor_cleanup(QapiCloneVisitor *v);
+
+Visitor *qapi_clone_get_visitor(QapiCloneVisitor *v);
+
+#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index f5d4b52..838e5d5 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -325,4 +325,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..42384d3
--- /dev/null
+++ b/qapi/qapi-clone-visitor.c
@@ -0,0 +1,166 @@
+/*
+ * Copy one QAPI object to another
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/visitor-impl.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) {
+ /* Nothing to allocate on the virtual walk during an
+ * alternate, but we still have to push depth.
+ * FIXME: passing obj to visit_end_struct would make this easier */
+ assert(qcv->depth);
+ qcv->depth++;
+ return;
+ }
+
+ *obj = g_memdup(qcv->depth ? *obj : qcv->root, size);
+ qcv->depth++;
+}
+
+static void qapi_clone_end(Visitor *v)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+ assert(qcv->depth);
+ 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_any(Visitor *v, const char *name, QObject **obj,
+ Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+ assert(qcv->depth);
+ /* Pointer was already copied by g_memdup; fix the refcount */
+ qobject_incref(*obj);
+}
+
+static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp)
+{
+ QapiCloneVisitor *qcv = to_qcv(v);
+ assert(qcv->depth);
+ /* Nothing to do */
+}
+
+Visitor *qapi_clone_get_visitor(QapiCloneVisitor *v)
+{
+ return &v->visitor;
+}
+
+void qapi_clone_visitor_cleanup(QapiCloneVisitor *v)
+{
+ g_free(v);
+}
+
+QapiCloneVisitor *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_any = qapi_clone_type_any;
+ v->visitor.type_null = qapi_clone_type_null;
+
+ return v;
+}
diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c
new file mode 100644
index 0000000..4aeaece
--- /dev/null
+++ b/tests/test-clone-visitor.c
@@ -0,0 +1,239 @@
+/*
+ * 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_UserDefOne_clone(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_AltStrBool_clone(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_AltStrBool_clone(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_uint8List_clone(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_Empty2_clone(src);
+ g_assert(dst);
+ qapi_free_Empty2(src);
+ qapi_free_Empty2(dst);
+}
+
+static void test_clone_complex1(void)
+{
+ UserDefNativeListUnion *src, *dst;
+ anyList *tmp;
+ QDict *dict;
+ QList *list;
+
+ src = g_new0(UserDefNativeListUnion, 1);
+ src->type = USER_DEF_NATIVE_LIST_UNION_KIND_STRING;
+
+ dst = qapi_UserDefNativeListUnion_clone(src);
+ g_assert(dst);
+ g_assert_cmpint(dst->type, ==, src->type);
+ g_assert(!dst->u.string.data);
+ qapi_free_UserDefNativeListUnion(dst);
+
+ src->type = USER_DEF_NATIVE_LIST_UNION_KIND_ANY;
+ tmp = src->u.any.data = g_new0(anyList, 1);
+ tmp->value = QOBJECT(qint_from_int(42));
+ tmp = tmp->next = g_new0(anyList, 1);
+ tmp->value = QOBJECT(dict = qdict_new());
+ qdict_put(dict, "key", qstring_from_str("value"));
+ tmp = tmp->next = g_new0(anyList, 1);
+ tmp->value = QOBJECT(qlist_new());
+
+ dst = qapi_UserDefNativeListUnion_clone(src);
+ g_assert(dst);
+ g_assert_cmpint(dst->type, ==, src->type);
+ tmp = dst->u.any.data;
+ g_assert(tmp);
+ g_assert_cmpint(qobject_type(tmp->value), ==, QTYPE_QINT);
+ g_assert_cmpint(qint_get_int(qobject_to_qint(tmp->value)), ==, 42);
+ tmp = tmp->next;
+ g_assert(tmp);
+ g_assert_cmpint(qobject_type(tmp->value), ==, QTYPE_QDICT);
+ dict = qobject_to_qdict(tmp->value);
+ g_assert_cmpint(qdict_size(dict), ==, 1);
+ g_assert_cmpstr(qdict_get_str(dict, "key"), ==, "value");
+ tmp = tmp->next;
+ g_assert(tmp);
+ g_assert_cmpint(qobject_type(tmp->value), ==, QTYPE_QLIST);
+ list = qobject_to_qlist(tmp->value);
+ g_assert_cmpint(qlist_size(list), ==, 0);
+ tmp = tmp->next;
+ g_assert(!tmp);
+
+ 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;
+ src->alt->u.udfu.string = g_strdup("Hello");
+ 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_WrapAlternate_clone(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, ==, "Hello");
+ 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___org_qemu_x_Struct2_clone(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/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index d7d6987..92fbb0e 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -787,6 +787,8 @@ Example:
char *string;
};
+ UserDefOne *qapi_UserDefOne_clone(const UserDefOne *src);
+
void qapi_free_UserDefOne(UserDefOne *obj);
struct UserDefOneList {
@@ -794,12 +796,31 @@ Example:
UserDefOne *value;
};
+ UserDefOneList *qapi_UserDefOneList_clone(const UserDefOneList *src);
+
void qapi_free_UserDefOneList(UserDefOneList *obj);
#endif
$ cat qapi-generated/example-qapi-types.c
[Uninteresting stuff omitted...]
+ UserDefOne *qapi_UserDefOne_clone(const UserDefOne *src)
+ {
+ QapiCloneVisitor *qcv;
+ Visitor *v;
+ UserDefOne *dst;
+
+ if (!src) {
+ return;
+ }
+
+ qcv = qapi_clone_visitor_new(src);
+ v = qapi_clone_get_visitor(qcv);
+ visit_type_UserDefOne(v, NULL, &dst, NULL);
+ qapi_clone_visitor_cleanup(qcv);
+ return dst;
+ }
+
void qapi_free_UserDefOne(UserDefOne *obj)
{
QapiDeallocVisitor *qdv;
@@ -815,6 +836,23 @@ Example:
qapi_dealloc_visitor_cleanup(qdv);
}
+ UserDefOneList *qapi_UserDefOneList_clone(const UserDefOneList *src)
+ {
+ QapiCloneVisitor *qcv;
+ Visitor *v;
+ UserDefOneList *dst;
+
+ if (!dst) {
+ return;
+ }
+
+ qcv = qapi_clone_visitor_new(src);
+ v = qapi_clone_get_visitor(qcv);
+ visit_type_UserDefOneList(v, NULL, &dst, NULL);
+ qapi_clone_visitor_cleanup(qcv);
+ return dst;
+ }
+
void qapi_free_UserDefOneList(UserDefOneList *obj)
{
QapiDeallocVisitor *qdv;
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index b60e11b..1406df7 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 json-output-visitor.o
+util-obj-y += opts-visitor.o json-output-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 c2aad79..60ff7cc 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 1f8a39d..10ed072 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-json-output-visitor$(EXESUF)
gcov-files-test-json-output-visitor-y = qapi/json-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)
@@ -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-json-output-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-json-output-visitor$(EXESUF): tests/test-json-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
- [Qemu-devel] [PATCH v3 08/18] qjson: Simplify by using json-output-visitor, (continued)
- [Qemu-devel] [PATCH v3 08/18] qjson: Simplify by using json-output-visitor, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 02/18] qapi: Improve use of qmp/types.h, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 06/18] qapi: Add qstring_append_format(), Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 01/18] qapi: Rename (one) qjson.h to qobject-json.h, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 11/18] qjson: Remove unused file, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 16/18] sockets: Use new QAPI cloning, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 04/18] qapi: Factor out JSON number formatting, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 07/18] qapi: Add json output visitor, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 10/18] vmstate: Use new JSON output visitor, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 15/18] qapi: Add new clone visitor,
Eric Blake <=
- [Qemu-devel] [PATCH v3 13/18] qapi: Support pretty printing in JSON output visitor, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 14/18] qemu-img: Use new JSON output formatter, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 12/18] qapi: Add qobject_to_json_pretty_prefix(), Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 17/18] replay: Use new QAPI cloning, Eric Blake, 2016/04/29
- [Qemu-devel] [PATCH v3 18/18] qapi: Add parameter to visit_end_*, Eric Blake, 2016/04/29