diff mbox

[RFC,v2,40/47] qapi: Introduce a first class 'any' type

Message ID 1435782155-31412-41-git-send-email-armbru@redhat.com
State New
Headers show

Commit Message

Markus Armbruster July 1, 2015, 8:22 p.m. UTC
It's first class, because unlike '**', it actually works, i.e. doesn't
require 'gen': false.

'**' will go away next.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 docs/qapi-code-gen.txt            |  1 +
 include/qapi/visitor-impl.h       |  2 ++
 include/qapi/visitor.h            |  1 +
 qapi/qapi-dealloc-visitor.c       |  9 +++++++
 qapi/qapi-visit-core.c            |  6 +++++
 qapi/qmp-input-visitor.c          | 11 ++++++++
 qapi/qmp-output-visitor.c         |  9 +++++++
 scripts/qapi-types.py             |  1 +
 scripts/qapi.py                   |  9 ++++---
 tests/qapi-schema/type-bypass.out |  4 +--
 tests/test-qmp-input-visitor.c    | 45 +++++++++++++++++++++++++++++++++
 tests/test-qmp-output-visitor.c   | 53 +++++++++++++++++++++++++++++++++++++++
 12 files changed, 146 insertions(+), 5 deletions(-)

Comments

Eric Blake July 23, 2015, 10:04 p.m. UTC | #1
On 07/01/2015 02:22 PM, Markus Armbruster wrote:
> It's first class, because unlike '**', it actually works, i.e. doesn't
> require 'gen': false.

Generated code grows in order to accommodate visiting the new anyList type:

 qapi-types.c                        |   15 +++++++++++++++
 qapi-types.h                        |   13 +++++++++++++
 qapi-visit.c                        |   24 ++++++++++++++++++++++++
 qapi-visit.h                        |    1 +
 qga/qapi-generated/qga-qapi-types.h |   13 +++++++++++++
 qga/qapi-generated/qga-qapi-visit.h |    1 +
 6 files changed, 67 insertions(+)

But do we really need anyList as a representation of qapi ['any'] ?  Or
will a later pass that minimizes array creation only to places where it
is needed help?

> 
> '**' will go away next.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>  docs/qapi-code-gen.txt            |  1 +
>  include/qapi/visitor-impl.h       |  2 ++
>  include/qapi/visitor.h            |  1 +
>  qapi/qapi-dealloc-visitor.c       |  9 +++++++
>  qapi/qapi-visit-core.c            |  6 +++++
>  qapi/qmp-input-visitor.c          | 11 ++++++++
>  qapi/qmp-output-visitor.c         |  9 +++++++
>  scripts/qapi-types.py             |  1 +
>  scripts/qapi.py                   |  9 ++++---
>  tests/qapi-schema/type-bypass.out |  4 +--
>  tests/test-qmp-input-visitor.c    | 45 +++++++++++++++++++++++++++++++++
>  tests/test-qmp-output-visitor.c   | 53 +++++++++++++++++++++++++++++++++++++++
>  12 files changed, 146 insertions(+), 5 deletions(-)

Touches a bit more this time, but that's okay.

> 
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index cb0fe75..bf9e854 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -158,6 +158,7 @@ The following types are predefined, and map to C as follows:
>    size      uint64_t   like uint64_t, except StringInputVisitor
>                         accepts size suffixes
>    bool      bool       JSON true or false
> +  any       QObject *  any JSON value

And with this, an 'alternate' type is just a special subset of 'any'
that limits the set of valid JSON values according to the qapi description.

> +++ b/qapi/qapi-visit-core.c
> @@ -260,6 +260,12 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
>      v->type_number(v, obj, name, errp);
>  }
>  
> +void visit_type_any(Visitor *v, QObject **obj, const char *name,
> +                         Error **errp)

Indentation looks off.

> @@ -1048,8 +1048,9 @@ class QAPISchema(object):
>                    ('uint64', 'int',     'uint64_t', '0'),
>                    ('size',   'int',     'uint64_t', '0'),
>                    ('bool',   'boolean', 'bool',     'false'),
> -                  ('**',     'value',   None,       None)]:
> +                  ('any',    'value',   'QObject' + pointer_suffix , 'NULL')]:
>              self._def_builtin_type(*t)
> +        self.entity_dict['**'] = self.lookup_type('any') # TODO drop this alias
>  
>      def _make_implicit_enum_type(self, name, values):
>          name = name + 'Kind'
> @@ -1190,6 +1191,8 @@ class QAPISchema(object):
>      def visit(self, visitor):
>          visitor.visit_begin()
>          for name in sorted(self.entity_dict.keys()):
> +            if self.entity_dict[name].name != name:
> +                continue        # ignore alias TODO drop alias and remove

Nice back-compat hacks while you transition into using it.

> +++ b/tests/test-qmp-input-visitor.c
> @@ -298,6 +298,49 @@ static void test_visitor_in_list(TestInputVisitorData *data,
>      qapi_free_UserDefOneList(head);
>  }
>  
> +static void test_visitor_in_any(TestInputVisitorData *data,
> +                                const void *unused)
> +{

> +
> +    v = visitor_input_test_init(data, "-42");

So we prove it accepts ints,...

> +    v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");

objects,...

> +    visit_type_any(v, &res, NULL, &err);
> +    g_assert(!err);
> +    qdict = qobject_to_qdict(res);
> +    g_assert(qdict);

worth asserting the dictionary has 3 keys?

> +}

...but no test of string, boolean, or array?

> +++ b/tests/test-qmp-output-visitor.c
> @@ -428,6 +428,57 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
>      qapi_free_UserDefTwoList(head);
>  }
>  
> +static void test_visitor_out_any(TestOutputVisitorData *data,
> +                                 const void *unused)
> +{

> +}

Same tests of 'int' and 'object' but not of other JSON types.

Incomplete tests are still better than no tests, so what you have is a
good start.

Reviewed-by: Eric Blake <eblake@redhat.com>
Markus Armbruster July 28, 2015, 11:31 a.m. UTC | #2
Eric Blake <eblake@redhat.com> writes:

> On 07/01/2015 02:22 PM, Markus Armbruster wrote:
>> It's first class, because unlike '**', it actually works, i.e. doesn't
>> require 'gen': false.
>
> Generated code grows in order to accommodate visiting the new anyList type:
>
>  qapi-types.c                        |   15 +++++++++++++++
>  qapi-types.h                        |   13 +++++++++++++
>  qapi-visit.c                        |   24 ++++++++++++++++++++++++
>  qapi-visit.h                        |    1 +
>  qga/qapi-generated/qga-qapi-types.h |   13 +++++++++++++
>  qga/qapi-generated/qga-qapi-visit.h |    1 +
>  6 files changed, 67 insertions(+)
>
> But do we really need anyList as a representation of qapi ['any'] ?  Or
> will a later pass that minimizes array creation only to places where it
> is needed help?

Like most generated list types, anyList isn't used.  I didn't feel like
making it a special case.  If the unused list types bother us, we can
generate only the ones actually mentioned in the schema.  May require
mentioning a few we use internally just to have them generated.

>> '**' will go away next.
>> 
>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> ---
>>  docs/qapi-code-gen.txt            |  1 +
>>  include/qapi/visitor-impl.h       |  2 ++
>>  include/qapi/visitor.h            |  1 +
>>  qapi/qapi-dealloc-visitor.c       |  9 +++++++
>>  qapi/qapi-visit-core.c            |  6 +++++
>>  qapi/qmp-input-visitor.c          | 11 ++++++++
>>  qapi/qmp-output-visitor.c         |  9 +++++++
>>  scripts/qapi-types.py             |  1 +
>>  scripts/qapi.py                   |  9 ++++---
>>  tests/qapi-schema/type-bypass.out |  4 +--
>>  tests/test-qmp-input-visitor.c    | 45 +++++++++++++++++++++++++++++++++
>>  tests/test-qmp-output-visitor.c | 53
>> +++++++++++++++++++++++++++++++++++++++
>>  12 files changed, 146 insertions(+), 5 deletions(-)
>
> Touches a bit more this time, but that's okay.
>
>> 
>> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
>> index cb0fe75..bf9e854 100644
>> --- a/docs/qapi-code-gen.txt
>> +++ b/docs/qapi-code-gen.txt
>> @@ -158,6 +158,7 @@ The following types are predefined, and map to C as follows:
>>    size      uint64_t   like uint64_t, except StringInputVisitor
>>                         accepts size suffixes
>>    bool      bool       JSON true or false
>> +  any       QObject *  any JSON value
>
> And with this, an 'alternate' type is just a special subset of 'any'
> that limits the set of valid JSON values according to the qapi description.
>
>> +++ b/qapi/qapi-visit-core.c
>> @@ -260,6 +260,12 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
>>      v->type_number(v, obj, name, errp);
>>  }
>>  
>> +void visit_type_any(Visitor *v, QObject **obj, const char *name,
>> +                         Error **errp)
>
> Indentation looks off.

Will fix.

>> @@ -1048,8 +1048,9 @@ class QAPISchema(object):
>>                    ('uint64', 'int',     'uint64_t', '0'),
>>                    ('size',   'int',     'uint64_t', '0'),
>>                    ('bool',   'boolean', 'bool',     'false'),
>> -                  ('**',     'value',   None,       None)]:
>> +                  ('any',    'value',   'QObject' + pointer_suffix , 'NULL')]:
>>              self._def_builtin_type(*t)
>> +        self.entity_dict['**'] = self.lookup_type('any') # TODO drop this alias
>>  
>>      def _make_implicit_enum_type(self, name, values):
>>          name = name + 'Kind'
>> @@ -1190,6 +1191,8 @@ class QAPISchema(object):
>>      def visit(self, visitor):
>>          visitor.visit_begin()
>>          for name in sorted(self.entity_dict.keys()):
>> +            if self.entity_dict[name].name != name:
>> +                continue        # ignore alias TODO drop alias and remove
>
> Nice back-compat hacks while you transition into using it.
>
>> +++ b/tests/test-qmp-input-visitor.c
>> @@ -298,6 +298,49 @@ static void test_visitor_in_list(TestInputVisitorData *data,
>>      qapi_free_UserDefOneList(head);
>>  }
>>  
>> +static void test_visitor_in_any(TestInputVisitorData *data,
>> +                                const void *unused)
>> +{
>
>> +
>> +    v = visitor_input_test_init(data, "-42");
>
> So we prove it accepts ints,...
>
>> + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean':
>> true, 'string': 'foo' }");
>
> objects,...
>
>> +    visit_type_any(v, &res, NULL, &err);
>> +    g_assert(!err);
>> +    qdict = qobject_to_qdict(res);
>> +    g_assert(qdict);
>
> worth asserting the dictionary has 3 keys?

Can't hurt.

>> +}
>
> ...but no test of string, boolean, or array?

I cut corners to get the thing out: one scalar and one non-scalar type.

We can always add more.

>> +++ b/tests/test-qmp-output-visitor.c
>> @@ -428,6 +428,57 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
>>      qapi_free_UserDefTwoList(head);
>>  }
>>  
>> +static void test_visitor_out_any(TestOutputVisitorData *data,
>> +                                 const void *unused)
>> +{
>
>> +}
>
> Same tests of 'int' and 'object' but not of other JSON types.
>
> Incomplete tests are still better than no tests, so what you have is a
> good start.
>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Thanks!
diff mbox

Patch

diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index cb0fe75..bf9e854 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -158,6 +158,7 @@  The following types are predefined, and map to C as follows:
   size      uint64_t   like uint64_t, except StringInputVisitor
                        accepts size suffixes
   bool      bool       JSON true or false
+  any       QObject *  any JSON value
 
 
 === Includes ===
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index f4a2f74..8c0ba57 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -40,6 +40,8 @@  struct Visitor
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
                         Error **errp);
+    void (*type_any)(Visitor *v, QObject **obj, const char *name,
+                     Error **errp);
 
     /* May be NULL */
     void (*optional)(Visitor *v, bool *present, const char *name,
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 00ba104..cfc19a6 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -58,6 +58,7 @@  void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp);
 bool visit_start_union(Visitor *v, bool data_present, Error **errp);
 void visit_end_union(Visitor *v, bool data_present, Error **errp);
 
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index d7f92c5..737deab 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -151,6 +151,14 @@  static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
 {
 }
 
+static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
+                                       const char *name, Error **errp)
+{
+    if (obj) {
+        qobject_decref(*obj);
+    }
+}
+
 static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
                                    Error **errp)
 {
@@ -216,6 +224,7 @@  QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_bool = qapi_dealloc_type_bool;
     v->visitor.type_str = qapi_dealloc_type_str;
     v->visitor.type_number = qapi_dealloc_type_number;
+    v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_size = qapi_dealloc_type_size;
     v->visitor.start_union = qapi_dealloc_start_union;
 
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 5a7c900..230d4b2 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -260,6 +260,12 @@  void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
     v->type_number(v, obj, name, errp);
 }
 
+void visit_type_any(Visitor *v, QObject **obj, const char *name,
+                         Error **errp)
+{
+    v->type_any(v, obj, name, errp);
+}
+
 void output_type_enum(Visitor *v, int *obj, const char * const strings[],
                       const char *kind, const char *name,
                       Error **errp)
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index e97b8a4..5dd9ed5 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -286,6 +286,16 @@  static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
     }
 }
 
+static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
+                               Error **errp)
+{
+    QmpInputVisitor *qiv = to_qiv(v);
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+
+    qobject_incref(qobj);
+    *obj = qobj;
+}
+
 static void qmp_input_optional(Visitor *v, bool *present, const char *name,
                                Error **errp)
 {
@@ -329,6 +339,7 @@  QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
     v->visitor.type_bool = qmp_input_type_bool;
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
+    v->visitor.type_any = qmp_input_type_any;
     v->visitor.optional = qmp_input_optional;
     v->visitor.get_next_type = qmp_input_get_next_type;
 
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index efc19d5..1e36a7a 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -186,6 +186,14 @@  static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
     qmp_output_add(qov, name, qfloat_from_double(*obj));
 }
 
+static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
+                                Error **errp)
+{
+    QmpOutputVisitor *qov = to_qov(v);
+    qobject_incref(*obj);
+    qmp_output_add_obj(qov, name, *obj);
+}
+
 QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 {
     QObject *obj = qmp_output_first(qov);
@@ -233,6 +241,7 @@  QmpOutputVisitor *qmp_output_visitor_new(void)
     v->visitor.type_bool = qmp_output_type_bool;
     v->visitor.type_str = qmp_output_type_str;
     v->visitor.type_number = qmp_output_type_number;
+    v->visitor.type_any = qmp_output_type_any;
 
     QTAILQ_INIT(&v->stack);
 
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index b6990a5..7c403c0 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -289,6 +289,7 @@  fdef.write(mcgen('''
 fdecl.write(mcgen('''
 #include <stdbool.h>
 #include <stdint.h>
+#include "qapi/qmp/qobject.h"
 '''))
 
 schema = QAPISchema(input_file)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index c6a5ddc..2ff087f 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -33,6 +33,7 @@  builtin_types = {
     'uint32':   'QTYPE_QINT',
     'uint64':   'QTYPE_QINT',
     'size':     'QTYPE_QINT',
+    'any':      None,           # any qtype_code possible, actually
 }
 
 # Whitelist of commands allowed to return a non-dictionary
@@ -1031,8 +1032,7 @@  class QAPISchema(object):
 
     def _def_builtin_type(self, name, json_type, c_type, c_null):
         self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type, c_null))
-        if name != '**':
-            self._make_array_type(name) # TODO really needed?
+        self._make_array_type(name) # TODO really needed?
 
     def _def_predefineds(self):
         for t in [('str',    'string',  'char' + pointer_suffix, 'NULL'),
@@ -1048,8 +1048,9 @@  class QAPISchema(object):
                   ('uint64', 'int',     'uint64_t', '0'),
                   ('size',   'int',     'uint64_t', '0'),
                   ('bool',   'boolean', 'bool',     'false'),
-                  ('**',     'value',   None,       None)]:
+                  ('any',    'value',   'QObject' + pointer_suffix , 'NULL')]:
             self._def_builtin_type(*t)
+        self.entity_dict['**'] = self.lookup_type('any') # TODO drop this alias
 
     def _make_implicit_enum_type(self, name, values):
         name = name + 'Kind'
@@ -1190,6 +1191,8 @@  class QAPISchema(object):
     def visit(self, visitor):
         visitor.visit_begin()
         for name in sorted(self.entity_dict.keys()):
+            if self.entity_dict[name].name != name:
+                continue        # ignore alias TODO drop alias and remove
             self.entity_dict[name].visit(visitor)
         visitor.visit_end()
 
diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out
index b147864..2dc0c09 100644
--- a/tests/qapi-schema/type-bypass.out
+++ b/tests/qapi-schema/type-bypass.out
@@ -1,4 +1,4 @@ 
 object :obj-unsafe-args
-    member arg: ** optional=False
-command unsafe :obj-unsafe-args -> **
+    member arg: any optional=False
+command unsafe :obj-unsafe-args -> any
    gen=False success_response=True
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 56da3c6..9c4bfdf 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -298,6 +298,49 @@  static void test_visitor_in_list(TestInputVisitorData *data,
     qapi_free_UserDefOneList(head);
 }
 
+static void test_visitor_in_any(TestInputVisitorData *data,
+                                const void *unused)
+{
+    QObject *res = NULL;
+    Error *err = NULL;
+    Visitor *v;
+    QInt *qint;
+    QBool *qbool;
+    QString *qstring;
+    QDict *qdict;
+    QObject *qobj;
+
+    v = visitor_input_test_init(data, "-42");
+    visit_type_any(v, &res, NULL, &err);
+    g_assert(!err);
+    qint = qobject_to_qint(res);
+    g_assert(qint);
+    g_assert_cmpint(qint_get_int(qint), ==, -42);
+    qobject_decref(res);
+
+    v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
+    visit_type_any(v, &res, NULL, &err);
+    g_assert(!err);
+    qdict = qobject_to_qdict(res);
+    g_assert(qdict);
+    qobj = qdict_get(qdict, "integer");
+    g_assert(qobj);
+    qint = qobject_to_qint(qobj);
+    g_assert(qint);
+    g_assert_cmpint(qint_get_int(qint), ==, -42);
+    qobj = qdict_get(qdict, "boolean");
+    g_assert(qobj);
+    qbool = qobject_to_qbool(qobj);
+    g_assert(qbool);
+    g_assert(qbool_get_bool(qbool) == true);
+    qobj = qdict_get(qdict, "string");
+    g_assert(qobj);
+    qstring = qobject_to_qstring(qobj);
+    g_assert(qstring);
+    g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
+    qobject_decref(res);
+}
+
 static void test_visitor_in_union_flat(TestInputVisitorData *data,
                                        const void *unused)
 {
@@ -667,6 +710,8 @@  int main(int argc, char **argv)
                            &in_visitor_data, test_visitor_in_struct_nested);
     input_visitor_test_add("/visitor/input/list",
                            &in_visitor_data, test_visitor_in_list);
+    input_visitor_test_add("/visitor/input/any",
+                           &in_visitor_data, test_visitor_in_any);
     input_visitor_test_add("/visitor/input/union-flat",
                            &in_visitor_data, test_visitor_in_union_flat);
     input_visitor_test_add("/visitor/input/alternate",
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 338ada0..1a28dc2 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -428,6 +428,57 @@  static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
     qapi_free_UserDefTwoList(head);
 }
 
+static void test_visitor_out_any(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    QObject *qobj;
+    Error *err = NULL;
+    QInt *qint;
+    QBool *qbool;
+    QString *qstring;
+    QDict *qdict;
+    QObject *obj;
+
+    qobj = QOBJECT(qint_from_int(-42));
+    visit_type_any(data->ov, &qobj, NULL, &err);
+    g_assert(!err);
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QINT);
+    g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42);
+    qobject_decref(obj);
+    qobject_decref(qobj);
+
+    qdict = qdict_new();
+    qdict_put(qdict, "integer", qint_from_int(-42));
+    qdict_put(qdict, "boolean", qbool_from_bool(true));
+    qdict_put(qdict, "string", qstring_from_str("foo"));
+    qobj = QOBJECT(qdict);
+    visit_type_any(data->ov, &qobj, NULL, &err);
+    g_assert(!err);
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    qdict = qobject_to_qdict(obj);
+    g_assert(qdict);
+    qobj = qdict_get(qdict, "integer");
+    g_assert(qobj);
+    qint = qobject_to_qint(qobj);
+    g_assert(qint);
+    g_assert_cmpint(qint_get_int(qint), ==, -42);
+    qobj = qdict_get(qdict, "boolean");
+    g_assert(qobj);
+    qbool = qobject_to_qbool(qobj);
+    g_assert(qbool);
+    g_assert(qbool_get_bool(qbool) == true);
+    qobj = qdict_get(qdict, "string");
+    g_assert(qobj);
+    qstring = qobject_to_qstring(qobj);
+    g_assert(qstring);
+    g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
+    qobject_decref(obj);
+    qobject_decref(qobj);
+}
+
 static void test_visitor_out_union_flat(TestOutputVisitorData *data,
                                         const void *unused)
 {
@@ -832,6 +883,8 @@  int main(int argc, char **argv)
                             &out_visitor_data, test_visitor_out_struct_errors);
     output_visitor_test_add("/visitor/output/list",
                             &out_visitor_data, test_visitor_out_list);
+    output_visitor_test_add("/visitor/output/any",
+                            &out_visitor_data, test_visitor_out_any);
     output_visitor_test_add("/visitor/output/list-qapi-free",
                             &out_visitor_data, test_visitor_out_list_qapi_free);
     output_visitor_test_add("/visitor/output/union-flat",