The functions simplify the handling of QOM properties whose type
is a QAPI struct. They go through a QObject just like the other
functions that access a QOM property through its C type.
Like QAPI_CLONE, the functions are wrapped by macros that take a
QAPI type name and use it to build the name of a visitor function.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/qom/qom-qobject.h | 68 +++++++++++-
qom/qom-qobject.c | 52 ++++++++++
tests/Makefile.include | 2 +-
tests/check-qom-proplist.c | 177 +++++++++++++++++++++++++++++++-
tests/qapi-schema/qapi-schema-test.json | 8 ++
tests/qapi-schema/qapi-schema-test.out | 6 ++
6 files changed, 309 insertions(+), 4 deletions(-)
diff --git a/include/qom/qom-qobject.h b/include/qom/qom-qobject.h
index 77cd717..fab9c4f 100644
--- a/include/qom/qom-qobject.h
+++ b/include/qom/qom-qobject.h
@@ -30,7 +30,7 @@ struct QObject *object_property_get_qobject(Object *obj, const char *name,
/**
* object_property_set_qobject:
* @obj: the object
- * @ret: The value that will be written to the property.
+ * @ret: The value that will be written to the property
* @name: the name of the property
* @errp: returns an error if this function fails
*
@@ -39,4 +39,70 @@ struct QObject *object_property_get_qobject(Object *obj, const char *name,
void object_property_set_qobject(Object *obj, struct QObject *qobj,
const char *name, struct Error **errp);
+/**
+ * object_property_get_ptr:
+ * @obj: the object
+ * @name: the name of the property
+ * @visit_type: the visitor function for the returned object
+ * @errp: returns an error if this function fails
+ *
+ * Return: the value of an object's property, unmarshaled into a C object
+ * through a QAPI type visitor, or NULL if an error occurs.
+ */
+void *object_property_get_ptr(Object *obj, const char *name,
+ void (*visit_type)(Visitor *, const char *,
+ void **, Error **),
+ Error **errp);
+
+/**
+ * OBJECT_PROPERTY_GET_PTR:
+ * @obj: the object
+ * @name: the name of the property
+ * @type: the name of the C struct type that is returned
+ * @errp: returns an error if this function fails
+ *
+ * Return: the value of an object's property, unmarshaled into a C object
+ * through a QAPI type visitor, or NULL if an error occurs.
+ */
+#define OBJECT_PROPERTY_GET_PTR(obj, name, type, errp) \
+ ((type *) \
+ object_property_get_ptr(obj, name, \
+ (void (*)(Visitor *, const char *, void**, \
+ Error **))visit_type_ ## type, \
+ errp))
+
+/**
+ * object_property_set_ptr:
+ * @obj: the object
+ * @ptr: The C struct that will be written to the property
+ * @name: the name of the property
+ * @visit_type: the visitor function for @ptr's type
+ * @errp: returns an error if this function fails
+ *
+ * Sets an object's property to a C object's value, using a QAPI
+ * type visitor to marshal the C struct into the property value.
+ */
+void object_property_set_ptr(Object *obj, void *ptr, const char *name,
+ void (*visit_type)(Visitor *, const char *,
+ void **, Error **),
+ Error **errp);
+
+/**
+ * OBJECT_PROPERTY_SET_PTR:
+ * @obj: the object
+ * @ptr: The C struct that will be written to the property
+ * @name: the name of the property
+ * @type: the name of the C struct type pointed to by @ptr
+ * @errp: returns an error if this function fails
+ *
+ * Sets an object's property to a C object's value, using a QAPI
+ * type visitor to marshal the C struct into the property value.
+ */
+#define OBJECT_PROPERTY_SET_PTR(obj, ptr, name, type, errp) \
+ object_property_set_ptr(obj, ptr + type_check(type, typeof(*ptr)), \
+ name, \
+ (void (*)(Visitor *, const char *, void**, \
+ Error **))visit_type_ ## type, \
+ errp)
+
#endif
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index 447e4a0..d4675be 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -44,3 +44,55 @@ QObject *object_property_get_qobject(Object *obj, const char *name,
visit_free(v);
return ret;
}
+
+void object_property_set_ptr(Object *obj, void *ptr, const char *name,
+ void (*visit_type)(Visitor *, const char *,
+ void **, Error **),
+ Error **errp)
+{
+ Error *local_err = NULL;
+ QObject *ret = NULL;
+ Visitor *v;
+ v = qobject_output_visitor_new(&ret);
+ visit_type(v, name, &ptr, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ visit_free(v);
+ return;
+ }
+ visit_complete(v, &ret);
+ visit_free(v);
+
+ /* Do not use object_property_set_qobject until we switch it
+ * to use qobject_input_visitor_new in strict mode. See the
+ * /qom/proplist/get-set-ptr/contravariant unit test.
+ */
+ v = qobject_input_visitor_new(ret, true);
+ object_property_set(obj, v, name, errp);
+ visit_free(v);
+ qobject_decref(ret);
+}
+
+void *object_property_get_ptr(Object *obj, const char *name,
+ void (*visit_type)(Visitor *, const char *,
+ void **, Error **),
+ Error **errp)
+{
+ QObject *ret;
+ Visitor *v;
+ void *ptr = NULL;
+
+ ret = object_property_get_qobject(obj, name, errp);
+ if (!ret) {
+ return NULL;
+ }
+
+ /* Do not enable strict mode to allow passing covariant
+ * data types.
+ */
+ v = qobject_input_visitor_new(ret, false);
+ visit_type(v, name, &ptr, errp);
+ qobject_decref(ret);
+ visit_free(v);
+ return ptr;
+}
diff --git a/tests/Makefile.include b/tests/Makefile.include
index e60bb6c..1a1b6e2 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -519,7 +519,7 @@ tests/check-qfloat$(EXESUF): tests/check-qfloat.o $(test-util-obj-y)
tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y)
tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
-tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
+tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) $(test-qapi-obj-y)
tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y)
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
index a16cefc..1bf0320 100644
--- a/tests/check-qom-proplist.c
+++ b/tests/check-qom-proplist.c
@@ -22,8 +22,11 @@
#include "qapi/error.h"
#include "qom/object.h"
+#include "qom/qom-qobject.h"
#include "qemu/module.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
#define TYPE_DUMMY "qemu-dummy"
@@ -56,6 +59,8 @@ struct DummyObject {
bool bv;
DummyAnimal av;
char *sv;
+
+ UserDefOne *qv;
};
struct DummyObjectClass {
@@ -120,12 +125,42 @@ static char *dummy_get_sv(Object *obj,
static void dummy_init(Object *obj)
{
+ DummyObject *dobj = DUMMY_OBJECT(obj);
+
object_property_add_bool(obj, "bv",
dummy_get_bv,
dummy_set_bv,
NULL);
+ dobj->qv = g_new0(UserDefOne, 1);
+ dobj->qv->string = g_strdup("dummy string");
+}
+
+
+static void dummy_get_qv(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DummyObject *dobj = DUMMY_OBJECT(obj);
+
+ visit_type_UserDefOne(v, name, &dobj->qv, errp);
}
+static void dummy_set_qv(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ DummyObject *dobj = DUMMY_OBJECT(obj);
+ UserDefOne *qv = NULL;
+ Error *local_err = NULL;
+
+ visit_type_UserDefOne(v, name, &qv, &local_err);
+ if (local_err) {
+ g_assert(qv == NULL);
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ qapi_free_UserDefOne(dobj->qv);
+ dobj->qv = qv;
+}
static void dummy_class_init(ObjectClass *cls, void *data)
{
@@ -143,6 +178,13 @@ static void dummy_class_init(ObjectClass *cls, void *data)
dummy_get_av,
dummy_set_av,
NULL);
+ object_class_property_add(cls, "qv",
+ "UserDefOne",
+ dummy_get_qv,
+ dummy_set_qv,
+ NULL,
+ NULL,
+ NULL);
}
@@ -151,9 +193,9 @@ static void dummy_finalize(Object *obj)
DummyObject *dobj = DUMMY_OBJECT(obj);
g_free(dobj->sv);
+ qapi_free_UserDefOne(dobj->qv);
}
-
static const TypeInfo dummy_info = {
.name = TYPE_DUMMY,
.parent = TYPE_OBJECT,
@@ -473,7 +515,8 @@ static void test_dummy_iterator(void)
ObjectProperty *prop;
ObjectPropertyIterator iter;
- bool seenbv = false, seensv = false, seenav = false, seentype;
+ bool seenbv = false, seensv = false, seenav = false;
+ bool seenqv = false, seentype = false;
object_property_iter_init(&iter, OBJECT(dobj));
while ((prop = object_property_iter_next(&iter))) {
@@ -483,6 +526,8 @@ static void test_dummy_iterator(void)
seensv = true;
} else if (g_str_equal(prop->name, "av")) {
seenav = true;
+ } else if (g_str_equal(prop->name, "qv")) {
+ seenqv = true;
} else if (g_str_equal(prop->name, "type")) {
/* This prop comes from the base Object class */
seentype = true;
@@ -494,6 +539,7 @@ static void test_dummy_iterator(void)
g_assert(seenbv);
g_assert(seenav);
g_assert(seensv);
+ g_assert(seenqv);
g_assert(seentype);
object_unparent(OBJECT(dobj));
@@ -513,6 +559,129 @@ static void test_dummy_delchild(void)
object_unparent(OBJECT(dev));
}
+static void test_dummy_get_set_ptr_struct(void)
+{
+ DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
+ Error *local_err = NULL;
+ const char *s = "my other dummy string";
+ UserDefOne *ret;
+ UserDefOne val;
+
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
+ UserDefOne, &local_err);
+ g_assert(!local_err);
+
+ g_assert_cmpint(ret->integer, ==, 0);
+ g_assert_cmpstr(ret->string, ==, "dummy string");
+ g_assert(!ret->has_enum1);
+ qapi_free_UserDefOne(ret);
+
+ val.integer = 42;
+ val.string = g_strdup(s);
+ val.has_enum1 = true;
+ val.enum1 = ENUM_ONE_VALUE1;
+ OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
+ UserDefOne, &local_err);
+ g_assert(!local_err);
+
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
+ UserDefOne, &local_err);
+ g_assert(!local_err);
+
+ g_assert_cmpint(ret->integer, ==, val.integer);
+ g_assert_cmpstr(ret->string, ==, val.string);
+ g_assert(ret->has_enum1);
+ g_assert_cmpint(ret->enum1, ==, val.enum1);
+ g_free(val.string);
+ qapi_free_UserDefOne(ret);
+}
+
+static void test_dummy_get_set_ptr_contravariant(void)
+{
+ DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
+ Error *local_err = NULL;
+ UserDefOneMore *ret;
+ UserDefOneMore val;
+
+ /* You cannot retrieve a contravariant (subclass) type... */
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
+ UserDefOneMore, &local_err);
+ error_free_or_abort(&local_err);
+ g_assert(!ret);
+
+ /* And you cannot set one either. */
+ val.integer = 42;
+ val.string = g_strdup("unused");
+ val.has_enum1 = false;
+ val.boolean = false;
+
+ OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
+ UserDefOneMore, &local_err);
+ g_assert(local_err);
+}
+
+static void test_dummy_get_set_ptr_covariant(void)
+{
+ DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
+ Error *local_err = NULL;
+ UserDefZero *ret;
+ UserDefZero val;
+
+ /* You can retrieve a covariant (superclass) type... */
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
+ UserDefZero, &local_err);
+ g_assert(!local_err);
+
+ g_assert_cmpint(ret->integer, ==, 0);
+ qapi_free_UserDefZero(ret);
+
+ /* But you cannot set one. */
+ val.integer = 42;
+ OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
+ UserDefZero, &local_err);
+ error_free_or_abort(&local_err);
+
+ /* Test that the property has not been modified at all */
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
+ UserDefZero, &local_err);
+ g_assert(!local_err);
+
+ g_assert_cmpint(ret->integer, ==, 0);
+ qapi_free_UserDefZero(ret);
+}
+
+static void test_dummy_get_set_ptr_error(void)
+{
+ DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
+ Error *local_err = NULL;
+ const char *s = "my other dummy string";
+ UserDefOne *ret;
+ UserDefOne val;
+
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "blah",
+ UserDefOne, &local_err);
+ error_free_or_abort(&local_err);
+ g_assert(!ret);
+
+ val.integer = 42;
+ val.string = g_strdup(s);
+ val.has_enum1 = true;
+ val.enum1 = 100;
+ OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
+ UserDefOne, &local_err);
+ error_free_or_abort(&local_err);
+
+ ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
+ UserDefOne, &local_err);
+ g_assert(!local_err);
+
+ /* Test that the property has not been modified at all */
+ g_assert_cmpint(ret->integer, ==, 0);
+ g_assert_cmpstr(ret->string, ==, "dummy string");
+ g_assert(!ret->has_enum1);
+ qapi_free_UserDefOne(ret);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -530,5 +699,9 @@ int main(int argc, char **argv)
g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
+ g_test_add_func("/qom/proplist/get-set-ptr/struct", test_dummy_get_set_ptr_struct);
+ g_test_add_func("/qom/proplist/get-set-ptr/error", test_dummy_get_set_ptr_error);
+ g_test_add_func("/qom/proplist/get-set-ptr/covariant", test_dummy_get_set_ptr_covariant);
+ g_test_add_func("/qom/proplist/get-set-ptr/contravariant", test_dummy_get_set_ptr_contravariant);
return g_test_run();
}
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index f4d8cc4..4e3f6ff 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -91,6 +91,14 @@
'*enum1': 'EnumOne' } } # intentional forward reference
##
+# @UserDefOneMore:
+# for testing nested structs
+##
+{ 'struct': 'UserDefOneMore',
+ 'base': 'UserDefOne',
+ 'data': { 'boolean': 'bool' } }
+
+##
# @EnumOne:
##
{ 'enum': 'EnumOne',
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index bc8d496..d3a2990 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -107,6 +107,9 @@ object UserDefOne
base UserDefZero
member string: str optional=False
member enum1: EnumOne optional=True
+object UserDefOneMore
+ base UserDefOne
+ member boolean: bool optional=False
object UserDefOptions
member i64: intList optional=True
member u64: uint64List optional=True
@@ -283,6 +286,9 @@ for testing override of default naming heuristic
doc symbol=UserDefOne expr=('struct', 'UserDefOne')
body=
for testing nested structs
+doc symbol=UserDefOneMore expr=('struct', 'UserDefOneMore')
+ body=
+for testing nested structs
doc symbol=EnumOne expr=('enum', 'EnumOne')
doc symbol=UserDefZero expr=('struct', 'UserDefZero')
doc symbol=UserDefTwoDictDict expr=('struct', 'UserDefTwoDictDict')
--
2.9.3
On 02/22/2017 12:04 PM, Paolo Bonzini wrote: > The functions simplify the handling of QOM properties whose type > is a QAPI struct. They go through a QObject just like the other > functions that access a QOM property through its C type. > > Like QAPI_CLONE, the functions are wrapped by macros that take a > QAPI type name and use it to build the name of a visitor function. > > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- > include/qom/qom-qobject.h | 68 +++++++++++- > qom/qom-qobject.c | 52 ++++++++++ > tests/Makefile.include | 2 +- > tests/check-qom-proplist.c | 177 +++++++++++++++++++++++++++++++- > tests/qapi-schema/qapi-schema-test.json | 8 ++ > tests/qapi-schema/qapi-schema-test.out | 6 ++ > 6 files changed, 309 insertions(+), 4 deletions(-) > Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
Hi
On Wed, Feb 22, 2017 at 10:07 PM Paolo Bonzini <pbonzini@redhat.com> wrote:
> The functions simplify the handling of QOM properties whose type
> is a QAPI struct. They go through a QObject just like the other
> functions that access a QOM property through its C type.
>
> Like QAPI_CLONE, the functions are wrapped by macros that take a
> QAPI type name and use it to build the name of a visitor function.
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Please fix the tests leaks:
diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
index 1bf0320854..379d3abe6b 100644
--- a/tests/check-qom-proplist.c
+++ b/tests/check-qom-proplist.c
@@ -594,6 +594,7 @@ static void test_dummy_get_set_ptr_struct(void)
g_assert_cmpint(ret->enum1, ==, val.enum1);
g_free(val.string);
qapi_free_UserDefOne(ret);
+ object_unref(OBJECT(dobj));
}
static void test_dummy_get_set_ptr_contravariant(void)
@@ -617,7 +618,9 @@ static void test_dummy_get_set_ptr_contravariant(void)
OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
UserDefOneMore, &local_err);
- g_assert(local_err);
+ error_free_or_abort(&local_err);
+ object_unref(OBJECT(dobj));
+ g_free(val.string);
}
static void test_dummy_get_set_ptr_covariant(void)
@@ -648,6 +651,7 @@ static void test_dummy_get_set_ptr_covariant(void)
g_assert_cmpint(ret->integer, ==, 0);
qapi_free_UserDefZero(ret);
+ object_unref(OBJECT(dobj));
}
static void test_dummy_get_set_ptr_error(void)
@@ -680,6 +684,8 @@ static void test_dummy_get_set_ptr_error(void)
g_assert_cmpstr(ret->string, ==, "dummy string");
g_assert(!ret->has_enum1);
qapi_free_UserDefOne(ret);
+ object_unref(OBJECT(dobj));
+ g_free(val.string);
}
int main(int argc, char **argv)
> ---
> include/qom/qom-qobject.h | 68 +++++++++++-
> qom/qom-qobject.c | 52 ++++++++++
> tests/Makefile.include | 2 +-
> tests/check-qom-proplist.c | 177
> +++++++++++++++++++++++++++++++-
> tests/qapi-schema/qapi-schema-test.json | 8 ++
> tests/qapi-schema/qapi-schema-test.out | 6 ++
> 6 files changed, 309 insertions(+), 4 deletions(-)
>
> diff --git a/include/qom/qom-qobject.h b/include/qom/qom-qobject.h
> index 77cd717..fab9c4f 100644
> --- a/include/qom/qom-qobject.h
> +++ b/include/qom/qom-qobject.h
> @@ -30,7 +30,7 @@ struct QObject *object_property_get_qobject(Object *obj,
> const char *name,
> /**
> * object_property_set_qobject:
> * @obj: the object
> - * @ret: The value that will be written to the property.
> + * @ret: The value that will be written to the property
> * @name: the name of the property
> * @errp: returns an error if this function fails
> *
> @@ -39,4 +39,70 @@ struct QObject *object_property_get_qobject(Object
> *obj, const char *name,
> void object_property_set_qobject(Object *obj, struct QObject *qobj,
> const char *name, struct Error **errp);
>
> +/**
> + * object_property_get_ptr:
> + * @obj: the object
> + * @name: the name of the property
> + * @visit_type: the visitor function for the returned object
> + * @errp: returns an error if this function fails
> + *
> + * Return: the value of an object's property, unmarshaled into a C object
> + * through a QAPI type visitor, or NULL if an error occurs.
> + */
> +void *object_property_get_ptr(Object *obj, const char *name,
> + void (*visit_type)(Visitor *, const char *,
> + void **, Error **),
> + Error **errp);
> +
> +/**
> + * OBJECT_PROPERTY_GET_PTR:
> + * @obj: the object
> + * @name: the name of the property
> + * @type: the name of the C struct type that is returned
> + * @errp: returns an error if this function fails
> + *
> + * Return: the value of an object's property, unmarshaled into a C object
> + * through a QAPI type visitor, or NULL if an error occurs.
> + */
> +#define OBJECT_PROPERTY_GET_PTR(obj, name, type, errp)
> \
> + ((type *)
> \
> + object_property_get_ptr(obj, name,
> \
> + (void (*)(Visitor *, const char *, void**,
> \
> + Error **))visit_type_ ## type,
> \
> + errp))
> +
> +/**
> + * object_property_set_ptr:
> + * @obj: the object
> + * @ptr: The C struct that will be written to the property
> + * @name: the name of the property
> + * @visit_type: the visitor function for @ptr's type
> + * @errp: returns an error if this function fails
> + *
> + * Sets an object's property to a C object's value, using a QAPI
> + * type visitor to marshal the C struct into the property value.
> + */
> +void object_property_set_ptr(Object *obj, void *ptr, const char *name,
> + void (*visit_type)(Visitor *, const char *,
> + void **, Error **),
> + Error **errp);
> +
> +/**
> + * OBJECT_PROPERTY_SET_PTR:
> + * @obj: the object
> + * @ptr: The C struct that will be written to the property
> + * @name: the name of the property
> + * @type: the name of the C struct type pointed to by @ptr
> + * @errp: returns an error if this function fails
> + *
> + * Sets an object's property to a C object's value, using a QAPI
> + * type visitor to marshal the C struct into the property value.
> + */
> +#define OBJECT_PROPERTY_SET_PTR(obj, ptr, name, type, errp)
> \
> + object_property_set_ptr(obj, ptr + type_check(type, typeof(*ptr)),
> \
> + name,
> \
> + (void (*)(Visitor *, const char *, void**,
> \
> + Error **))visit_type_ ## type,
> \
> + errp)
> +
> #endif
> diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
> index 447e4a0..d4675be 100644
> --- a/qom/qom-qobject.c
> +++ b/qom/qom-qobject.c
> @@ -44,3 +44,55 @@ QObject *object_property_get_qobject(Object *obj, const
> char *name,
> visit_free(v);
> return ret;
> }
> +
> +void object_property_set_ptr(Object *obj, void *ptr, const char *name,
> + void (*visit_type)(Visitor *, const char *,
> + void **, Error **),
> + Error **errp)
> +{
> + Error *local_err = NULL;
> + QObject *ret = NULL;
> + Visitor *v;
> + v = qobject_output_visitor_new(&ret);
> + visit_type(v, name, &ptr, &local_err);
> + if (local_err) {
> + error_propagate(errp, local_err);
> + visit_free(v);
> + return;
> + }
> + visit_complete(v, &ret);
> + visit_free(v);
> +
> + /* Do not use object_property_set_qobject until we switch it
> + * to use qobject_input_visitor_new in strict mode. See the
> + * /qom/proplist/get-set-ptr/contravariant unit test.
> + */
> + v = qobject_input_visitor_new(ret, true);
> + object_property_set(obj, v, name, errp);
> + visit_free(v);
> + qobject_decref(ret);
> +}
> +
> +void *object_property_get_ptr(Object *obj, const char *name,
> + void (*visit_type)(Visitor *, const char *,
> + void **, Error **),
> + Error **errp)
> +{
> + QObject *ret;
> + Visitor *v;
> + void *ptr = NULL;
> +
> + ret = object_property_get_qobject(obj, name, errp);
> + if (!ret) {
> + return NULL;
> + }
> +
> + /* Do not enable strict mode to allow passing covariant
> + * data types.
> + */
> + v = qobject_input_visitor_new(ret, false);
> + visit_type(v, name, &ptr, errp);
> + qobject_decref(ret);
> + visit_free(v);
> + return ptr;
> +}
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index e60bb6c..1a1b6e2 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -519,7 +519,7 @@ tests/check-qfloat$(EXESUF): tests/check-qfloat.o
> $(test-util-obj-y)
> tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y)
> tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
> tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o
> $(test-qom-obj-y)
> -tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o
> $(test-qom-obj-y)
> +tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o
> $(test-qom-obj-y) $(test-qapi-obj-y)
>
> tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y)
> $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y)
> tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
> diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
> index a16cefc..1bf0320 100644
> --- a/tests/check-qom-proplist.c
> +++ b/tests/check-qom-proplist.c
> @@ -22,8 +22,11 @@
>
> #include "qapi/error.h"
> #include "qom/object.h"
> +#include "qom/qom-qobject.h"
> #include "qemu/module.h"
>
> +#include "test-qapi-types.h"
> +#include "test-qapi-visit.h"
>
> #define TYPE_DUMMY "qemu-dummy"
>
> @@ -56,6 +59,8 @@ struct DummyObject {
> bool bv;
> DummyAnimal av;
> char *sv;
> +
> + UserDefOne *qv;
> };
>
> struct DummyObjectClass {
> @@ -120,12 +125,42 @@ static char *dummy_get_sv(Object *obj,
>
> static void dummy_init(Object *obj)
> {
> + DummyObject *dobj = DUMMY_OBJECT(obj);
> +
> object_property_add_bool(obj, "bv",
> dummy_get_bv,
> dummy_set_bv,
> NULL);
> + dobj->qv = g_new0(UserDefOne, 1);
> + dobj->qv->string = g_strdup("dummy string");
> +}
> +
> +
> +static void dummy_get_qv(Object *obj, Visitor *v, const char *name,
> + void *opaque, Error **errp)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(obj);
> +
> + visit_type_UserDefOne(v, name, &dobj->qv, errp);
> }
>
> +static void dummy_set_qv(Object *obj, Visitor *v, const char *name,
> + void *opaque, Error **errp)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(obj);
> + UserDefOne *qv = NULL;
> + Error *local_err = NULL;
> +
> + visit_type_UserDefOne(v, name, &qv, &local_err);
> + if (local_err) {
> + g_assert(qv == NULL);
> + error_propagate(errp, local_err);
> + return;
> + }
> +
> + qapi_free_UserDefOne(dobj->qv);
> + dobj->qv = qv;
> +}
>
> static void dummy_class_init(ObjectClass *cls, void *data)
> {
> @@ -143,6 +178,13 @@ static void dummy_class_init(ObjectClass *cls, void
> *data)
> dummy_get_av,
> dummy_set_av,
> NULL);
> + object_class_property_add(cls, "qv",
> + "UserDefOne",
> + dummy_get_qv,
> + dummy_set_qv,
> + NULL,
> + NULL,
> + NULL);
> }
>
>
> @@ -151,9 +193,9 @@ static void dummy_finalize(Object *obj)
> DummyObject *dobj = DUMMY_OBJECT(obj);
>
> g_free(dobj->sv);
> + qapi_free_UserDefOne(dobj->qv);
> }
>
> -
> static const TypeInfo dummy_info = {
> .name = TYPE_DUMMY,
> .parent = TYPE_OBJECT,
> @@ -473,7 +515,8 @@ static void test_dummy_iterator(void)
>
> ObjectProperty *prop;
> ObjectPropertyIterator iter;
> - bool seenbv = false, seensv = false, seenav = false, seentype;
> + bool seenbv = false, seensv = false, seenav = false;
> + bool seenqv = false, seentype = false;
>
> object_property_iter_init(&iter, OBJECT(dobj));
> while ((prop = object_property_iter_next(&iter))) {
> @@ -483,6 +526,8 @@ static void test_dummy_iterator(void)
> seensv = true;
> } else if (g_str_equal(prop->name, "av")) {
> seenav = true;
> + } else if (g_str_equal(prop->name, "qv")) {
> + seenqv = true;
> } else if (g_str_equal(prop->name, "type")) {
> /* This prop comes from the base Object class */
> seentype = true;
> @@ -494,6 +539,7 @@ static void test_dummy_iterator(void)
> g_assert(seenbv);
> g_assert(seenav);
> g_assert(seensv);
> + g_assert(seenqv);
> g_assert(seentype);
>
> object_unparent(OBJECT(dobj));
> @@ -513,6 +559,129 @@ static void test_dummy_delchild(void)
> object_unparent(OBJECT(dev));
> }
>
> +static void test_dummy_get_set_ptr_struct(void)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
> + Error *local_err = NULL;
> + const char *s = "my other dummy string";
> + UserDefOne *ret;
> + UserDefOne val;
> +
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
> + UserDefOne, &local_err);
> + g_assert(!local_err);
> +
> + g_assert_cmpint(ret->integer, ==, 0);
> + g_assert_cmpstr(ret->string, ==, "dummy string");
> + g_assert(!ret->has_enum1);
> + qapi_free_UserDefOne(ret);
> +
> + val.integer = 42;
> + val.string = g_strdup(s);
> + val.has_enum1 = true;
> + val.enum1 = ENUM_ONE_VALUE1;
> + OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
> + UserDefOne, &local_err);
> + g_assert(!local_err);
> +
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
> + UserDefOne, &local_err);
> + g_assert(!local_err);
> +
> + g_assert_cmpint(ret->integer, ==, val.integer);
> + g_assert_cmpstr(ret->string, ==, val.string);
> + g_assert(ret->has_enum1);
> + g_assert_cmpint(ret->enum1, ==, val.enum1);
> + g_free(val.string);
> + qapi_free_UserDefOne(ret);
> +}
> +
> +static void test_dummy_get_set_ptr_contravariant(void)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
> + Error *local_err = NULL;
> + UserDefOneMore *ret;
> + UserDefOneMore val;
> +
> + /* You cannot retrieve a contravariant (subclass) type... */
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
> + UserDefOneMore, &local_err);
> + error_free_or_abort(&local_err);
> + g_assert(!ret);
> +
> + /* And you cannot set one either. */
> + val.integer = 42;
> + val.string = g_strdup("unused");
> + val.has_enum1 = false;
> + val.boolean = false;
> +
> + OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
> + UserDefOneMore, &local_err);
> + g_assert(local_err);
> +}
> +
> +static void test_dummy_get_set_ptr_covariant(void)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
> + Error *local_err = NULL;
> + UserDefZero *ret;
> + UserDefZero val;
> +
> + /* You can retrieve a covariant (superclass) type... */
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
> + UserDefZero, &local_err);
> + g_assert(!local_err);
> +
> + g_assert_cmpint(ret->integer, ==, 0);
> + qapi_free_UserDefZero(ret);
> +
> + /* But you cannot set one. */
> + val.integer = 42;
> + OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
> + UserDefZero, &local_err);
> + error_free_or_abort(&local_err);
> +
> + /* Test that the property has not been modified at all */
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
> + UserDefZero, &local_err);
> + g_assert(!local_err);
> +
> + g_assert_cmpint(ret->integer, ==, 0);
> + qapi_free_UserDefZero(ret);
> +}
> +
> +static void test_dummy_get_set_ptr_error(void)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(object_new(TYPE_DUMMY));
> + Error *local_err = NULL;
> + const char *s = "my other dummy string";
> + UserDefOne *ret;
> + UserDefOne val;
> +
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "blah",
> + UserDefOne, &local_err);
> + error_free_or_abort(&local_err);
> + g_assert(!ret);
> +
> + val.integer = 42;
> + val.string = g_strdup(s);
> + val.has_enum1 = true;
> + val.enum1 = 100;
> + OBJECT_PROPERTY_SET_PTR(OBJECT(dobj), &val, "qv",
> + UserDefOne, &local_err);
> + error_free_or_abort(&local_err);
> +
> + ret = OBJECT_PROPERTY_GET_PTR(OBJECT(dobj), "qv",
> + UserDefOne, &local_err);
> + g_assert(!local_err);
> +
> + /* Test that the property has not been modified at all */
> + g_assert_cmpint(ret->integer, ==, 0);
> + g_assert_cmpstr(ret->string, ==, "dummy string");
> + g_assert(!ret->has_enum1);
> + qapi_free_UserDefOne(ret);
> +}
> +
> int main(int argc, char **argv)
> {
> g_test_init(&argc, &argv, NULL);
> @@ -530,5 +699,9 @@ int main(int argc, char **argv)
> g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
> g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
>
> + g_test_add_func("/qom/proplist/get-set-ptr/struct",
> test_dummy_get_set_ptr_struct);
> + g_test_add_func("/qom/proplist/get-set-ptr/error",
> test_dummy_get_set_ptr_error);
> + g_test_add_func("/qom/proplist/get-set-ptr/covariant",
> test_dummy_get_set_ptr_covariant);
> + g_test_add_func("/qom/proplist/get-set-ptr/contravariant",
> test_dummy_get_set_ptr_contravariant);
> return g_test_run();
> }
> diff --git a/tests/qapi-schema/qapi-schema-test.json
> b/tests/qapi-schema/qapi-schema-test.json
> index f4d8cc4..4e3f6ff 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -91,6 +91,14 @@
> '*enum1': 'EnumOne' } } # intentional forward reference
>
> ##
> +# @UserDefOneMore:
> +# for testing nested structs
> +##
> +{ 'struct': 'UserDefOneMore',
> + 'base': 'UserDefOne',
> + 'data': { 'boolean': 'bool' } }
> +
> +##
> # @EnumOne:
> ##
> { 'enum': 'EnumOne',
> diff --git a/tests/qapi-schema/qapi-schema-test.out
> b/tests/qapi-schema/qapi-schema-test.out
> index bc8d496..d3a2990 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -107,6 +107,9 @@ object UserDefOne
> base UserDefZero
> member string: str optional=False
> member enum1: EnumOne optional=True
> +object UserDefOneMore
> + base UserDefOne
> + member boolean: bool optional=False
> object UserDefOptions
> member i64: intList optional=True
> member u64: uint64List optional=True
> @@ -283,6 +286,9 @@ for testing override of default naming heuristic
> doc symbol=UserDefOne expr=('struct', 'UserDefOne')
> body=
> for testing nested structs
> +doc symbol=UserDefOneMore expr=('struct', 'UserDefOneMore')
> + body=
> +for testing nested structs
> doc symbol=EnumOne expr=('enum', 'EnumOne')
> doc symbol=UserDefZero expr=('struct', 'UserDefZero')
> doc symbol=UserDefTwoDictDict expr=('struct', 'UserDefTwoDictDict')
> --
> 2.9.3
>
>
>
> --
Marc-André Lureau
© 2016 - 2026 Red Hat, Inc.