qemu-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]