From patchwork Fri Apr 29 04:23:36 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Blake X-Patchwork-Id: 8977071 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 7D14C9F39D for ; Fri, 29 Apr 2016 04:30:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B25612021B for ; Fri, 29 Apr 2016 04:30:22 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8784B2021A for ; Fri, 29 Apr 2016 04:30:20 +0000 (UTC) Received: from localhost ([::1]:52253 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aw04J-0003mc-TA for patchwork-qemu-devel@patchwork.kernel.org; Fri, 29 Apr 2016 00:30:19 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37875) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1avzy9-0007PB-DU for qemu-devel@nongnu.org; Fri, 29 Apr 2016 00:24:00 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1avzy6-0000up-D7 for qemu-devel@nongnu.org; Fri, 29 Apr 2016 00:23:57 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50352) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1avzy6-0000ud-3U for qemu-devel@nongnu.org; Fri, 29 Apr 2016 00:23:54 -0400 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C86ED3B71F; Fri, 29 Apr 2016 04:23:53 +0000 (UTC) Received: from red.redhat.com (ovpn-113-21.phx2.redhat.com [10.3.113.21]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u3T4NgfC016324; Fri, 29 Apr 2016 00:23:53 -0400 From: Eric Blake To: qemu-devel@nongnu.org Date: Thu, 28 Apr 2016 22:23:36 -0600 Message-Id: <1461903820-3092-16-git-send-email-eblake@redhat.com> In-Reply-To: <1461903820-3092-1-git-send-email-eblake@redhat.com> References: <1461903820-3092-1-git-send-email-eblake@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v3 15/18] qapi: Add new clone visitor X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: famz@redhat.com, armbru@redhat.com, Michael Roth Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 + +#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)