Hi On Wed, Feb 22, 2017 at 10:07 PM Paolo Bonzini <pbonz...@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 <pbonz...@redhat.com> > Reviewed-by: Marc-André Lureau <marcandre.lur...@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