218e99
From fafd52c6d2c32e24ae83b4d534c6e2f72f53795d Mon Sep 17 00:00:00 2001
218e99
From: Kevin Wolf <kwolf@redhat.com>
218e99
Date: Mon, 9 Sep 2013 14:28:16 +0200
218e99
Subject: [PATCH 25/38] qapi: Anonymous unions
218e99
218e99
RH-Author: Kevin Wolf <kwolf@redhat.com>
218e99
Message-id: <1378736903-18489-26-git-send-email-kwolf@redhat.com>
218e99
Patchwork-id: 54212
218e99
O-Subject: [RHEL-7.0 qemu-kvm PATCH 25/32] qapi: Anonymous unions
218e99
Bugzilla: 1005818
218e99
RH-Acked-by: Fam Zheng <famz@redhat.com>
218e99
RH-Acked-by: Max Reitz <mreitz@redhat.com>
218e99
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
218e99
218e99
Bugzilla: 1005818
218e99
218e99
The discriminator for anonymous unions is the data type. This allows to
218e99
have a union type that allows both of these:
218e99
218e99
    { 'file': 'my_existing_block_device_id' }
218e99
    { 'file': { 'filename': '/tmp/mydisk.qcow2', 'read-only': true } }
218e99
218e99
Unions like this are specified in the schema with an empty dict as
218e99
discriminator. For this example you could take:
218e99
218e99
    { 'union': 'BlockRef',
218e99
      'discriminator': {},
218e99
      'data': { 'definition': 'BlockOptions',
218e99
                'reference': 'str' } }
218e99
    { 'type': 'ExampleObject',
218e99
      'data: { 'file': 'BlockRef' } }
218e99
218e99
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
218e99
Reviewed-by: Eric Blake <eblake@redhat.com>
218e99
(cherry picked from commit 69dd62dfd60631ba69201d8a197fde1ece4b4df3)
218e99
218e99
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
218e99
---
218e99
 docs/qapi-code-gen.txt      | 25 ++++++++++++++++++++++++
218e99
 include/qapi/qmp/qobject.h  |  1 +
218e99
 include/qapi/visitor-impl.h |  2 ++
218e99
 include/qapi/visitor.h      |  3 +++
218e99
 qapi/qapi-visit-core.c      |  9 +++++++++
218e99
 qapi/qmp-input-visitor.c    | 14 ++++++++++++++
218e99
 qobject/qjson.c             |  2 ++
218e99
 scripts/qapi-types.py       | 42 ++++++++++++++++++++++++++++++++++++++++
218e99
 scripts/qapi-visit.py       | 47 +++++++++++++++++++++++++++++++++++++++++++++
218e99
 scripts/qapi.py             | 15 +++++++++++++++
218e99
 10 files changed, 160 insertions(+)
218e99
218e99
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
218e99
---
218e99
 docs/qapi-code-gen.txt      |   25 ++++++++++++++++++++++
218e99
 include/qapi/qmp/qobject.h  |    1 +
218e99
 include/qapi/visitor-impl.h |    2 +
218e99
 include/qapi/visitor.h      |    3 ++
218e99
 qapi/qapi-visit-core.c      |    9 ++++++++
218e99
 qapi/qmp-input-visitor.c    |   14 ++++++++++++
218e99
 qobject/qjson.c             |    2 +
218e99
 scripts/qapi-types.py       |   42 ++++++++++++++++++++++++++++++++++++++
218e99
 scripts/qapi-visit.py       |   47 +++++++++++++++++++++++++++++++++++++++++++
218e99
 scripts/qapi.py             |   15 +++++++++++++
218e99
 10 files changed, 160 insertions(+), 0 deletions(-)
218e99
218e99
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
218e99
index 11f19cf..0ce045c 100644
218e99
--- a/docs/qapi-code-gen.txt
218e99
+++ b/docs/qapi-code-gen.txt
218e99
@@ -125,6 +125,31 @@ Resulting in this JSON object:
218e99
    "lazy-refcounts": true }
218e99
 
218e99
 
218e99
+A special type of unions are anonymous unions. They don't form a dictionary in
218e99
+the wire format but allow the direct use of different types in their place. As
218e99
+they aren't structured, they don't have any explicit discriminator but use
218e99
+the (QObject) data type of their value as an implicit discriminator. This means
218e99
+that they are restricted to using only one discriminator value per QObject
218e99
+type. For example, you cannot have two different complex types in an anonymous
218e99
+union, or two different integer types.
218e99
+
218e99
+Anonymous unions are declared using an empty dictionary as their discriminator.
218e99
+The discriminator values never appear on the wire, they are only used in the
218e99
+generated C code. Anonymous unions cannot have a base type.
218e99
+
218e99
+ { 'union': 'BlockRef',
218e99
+   'discriminator': {},
218e99
+   'data': { 'definition': 'BlockdevOptions',
218e99
+             'reference': 'str' } }
218e99
+
218e99
+This example allows using both of the following example objects:
218e99
+
218e99
+ { "file": "my_existing_block_device_id" }
218e99
+ { "file": { "driver": "file",
218e99
+             "readonly": false,
218e99
+             'filename': "/tmp/mydisk.qcow2" } }
218e99
+
218e99
+
218e99
 === Commands ===
218e99
 
218e99
 Commands are defined by using a list containing three members.  The first
218e99
diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
218e99
index 9124649..d0bbc7c 100644
218e99
--- a/include/qapi/qmp/qobject.h
218e99
+++ b/include/qapi/qmp/qobject.h
218e99
@@ -44,6 +44,7 @@ typedef enum {
218e99
     QTYPE_QFLOAT,
218e99
     QTYPE_QBOOL,
218e99
     QTYPE_QERROR,
218e99
+    QTYPE_MAX,
218e99
 } qtype_code;
218e99
 
218e99
 struct QObject;
218e99
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
218e99
index 5c1297f..f3fa420 100644
218e99
--- a/include/qapi/visitor-impl.h
218e99
+++ b/include/qapi/visitor-impl.h
218e99
@@ -32,6 +32,8 @@ struct Visitor
218e99
 
218e99
     void (*type_enum)(Visitor *v, int *obj, const char *strings[],
218e99
                       const char *kind, const char *name, Error **errp);
218e99
+    void (*get_next_type)(Visitor *v, int *kind, const int *qobjects,
218e99
+                          const char *name, Error **errp);
218e99
 
218e99
     void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
218e99
     void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
218e99
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
218e99
index a1cdd81..aa9e771 100644
218e99
--- a/include/qapi/visitor.h
218e99
+++ b/include/qapi/visitor.h
218e99
@@ -13,6 +13,7 @@
218e99
 #ifndef QAPI_VISITOR_CORE_H
218e99
 #define QAPI_VISITOR_CORE_H
218e99
 
218e99
+#include "qapi/qmp/qobject.h"
218e99
 #include "qapi/error.h"
218e99
 #include <stdlib.h>
218e99
 
218e99
@@ -39,6 +40,8 @@ void visit_end_list(Visitor *v, Error **errp);
218e99
 void visit_start_optional(Visitor *v, bool *present, const char *name,
218e99
                           Error **errp);
218e99
 void visit_end_optional(Visitor *v, Error **errp);
218e99
+void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
218e99
+                         const char *name, Error **errp);
218e99
 void visit_type_enum(Visitor *v, int *obj, const char *strings[],
218e99
                      const char *kind, const char *name, Error **errp);
218e99
 void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
218e99
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
218e99
index 9b4d51b..d6a4012 100644
218e99
--- a/qapi/qapi-visit-core.c
218e99
+++ b/qapi/qapi-visit-core.c
218e99
@@ -12,6 +12,7 @@
218e99
  */
218e99
 
218e99
 #include "qemu-common.h"
218e99
+#include "qapi/qmp/qobject.h"
218e99
 #include "qapi/qmp/qerror.h"
218e99
 #include "qapi/visitor.h"
218e99
 #include "qapi/visitor-impl.h"
218e99
@@ -98,6 +99,14 @@ void visit_end_optional(Visitor *v, Error **errp)
218e99
     }
218e99
 }
218e99
 
218e99
+void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
218e99
+                         const char *name, Error **errp)
218e99
+{
218e99
+    if (!error_is_set(errp) && v->get_next_type) {
218e99
+        v->get_next_type(v, obj, qtypes, name, errp);
218e99
+    }
218e99
+}
218e99
+
218e99
 void visit_type_enum(Visitor *v, int *obj, const char *strings[],
218e99
                      const char *kind, const char *name, Error **errp)
218e99
 {
218e99
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
218e99
index 70864a1..bf42c04 100644
218e99
--- a/qapi/qmp-input-visitor.c
218e99
+++ b/qapi/qmp-input-visitor.c
218e99
@@ -208,6 +208,19 @@ static void qmp_input_end_list(Visitor *v, Error **errp)
218e99
     qmp_input_pop(qiv, errp);
218e99
 }
218e99
 
218e99
+static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects,
218e99
+                                    const char *name, Error **errp)
218e99
+{
218e99
+    QmpInputVisitor *qiv = to_qiv(v);
218e99
+    QObject *qobj = qmp_input_get_object(qiv, name, false);
218e99
+
218e99
+    if (!qobj) {
218e99
+        error_set(errp, QERR_MISSING_PARAMETER, name ? name : "null");
218e99
+        return;
218e99
+    }
218e99
+    *kind = qobjects[qobject_type(qobj)];
218e99
+}
218e99
+
218e99
 static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name,
218e99
                                Error **errp)
218e99
 {
218e99
@@ -317,6 +330,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
218e99
     v->visitor.type_str = qmp_input_type_str;
218e99
     v->visitor.type_number = qmp_input_type_number;
218e99
     v->visitor.start_optional = qmp_input_start_optional;
218e99
+    v->visitor.get_next_type = qmp_input_get_next_type;
218e99
 
218e99
     qmp_input_push(v, obj, NULL);
218e99
     qobject_incref(obj);
218e99
diff --git a/qobject/qjson.c b/qobject/qjson.c
218e99
index 19085a1..6cf2511 100644
218e99
--- a/qobject/qjson.c
218e99
+++ b/qobject/qjson.c
218e99
@@ -260,6 +260,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
218e99
         /* XXX: should QError be emitted? */
218e99
     case QTYPE_NONE:
218e99
         break;
218e99
+    case QTYPE_MAX:
218e99
+        abort();
218e99
     }
218e99
 }
218e99
 
218e99
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
218e99
index 3d9af3c..20f4bdf 100644
218e99
--- a/scripts/qapi-types.py
218e99
+++ b/scripts/qapi-types.py
218e99
@@ -144,6 +144,40 @@ typedef enum %(name)s
218e99
 
218e99
     return lookup_decl + enum_decl
218e99
 
218e99
+def generate_anon_union_qtypes(expr):
218e99
+
218e99
+    name = expr['union']
218e99
+    members = expr['data']
218e99
+
218e99
+    ret = mcgen('''
218e99
+const int %(name)s_qtypes[QTYPE_MAX] = {
218e99
+''',
218e99
+    name=name)
218e99
+
218e99
+    for key in members:
218e99
+        qapi_type = members[key]
218e99
+        if builtin_type_qtypes.has_key(qapi_type):
218e99
+            qtype = builtin_type_qtypes[qapi_type]
218e99
+        elif find_struct(qapi_type):
218e99
+            qtype = "QTYPE_QDICT"
218e99
+        elif find_union(qapi_type):
218e99
+            qtype = "QTYPE_QDICT"
218e99
+        else:
218e99
+            assert False, "Invalid anonymous union member"
218e99
+
218e99
+        ret += mcgen('''
218e99
+    [ %(qtype)s ] = %(abbrev)s_KIND_%(enum)s,
218e99
+''',
218e99
+        qtype = qtype,
218e99
+        abbrev = de_camel_case(name).upper(),
218e99
+        enum = c_fun(de_camel_case(key),False).upper())
218e99
+
218e99
+    ret += mcgen('''
218e99
+};
218e99
+''')
218e99
+    return ret
218e99
+
218e99
+
218e99
 def generate_union(expr):
218e99
 
218e99
     name = expr['union']
218e99
@@ -184,6 +218,12 @@ struct %(name)s
218e99
     ret += mcgen('''
218e99
 };
218e99
 ''')
218e99
+    if discriminator == {}:
218e99
+        ret += mcgen('''
218e99
+extern const int %(name)s_qtypes[];
218e99
+''',
218e99
+            name=name)
218e99
+
218e99
 
218e99
     return ret
218e99
 
218e99
@@ -336,6 +376,8 @@ for expr in exprs:
218e99
         ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
218e99
         ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
218e99
         fdef.write(generate_enum_lookup('%sKind' % expr['union'], expr['data'].keys()))
218e99
+        if expr.get('discriminator') == {}:
218e99
+            fdef.write(generate_anon_union_qtypes(expr))
218e99
     else:
218e99
         continue
218e99
     fdecl.write(ret)
218e99
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
218e99
index b1c1ad6..367cf7a 100644
218e99
--- a/scripts/qapi-visit.py
218e99
+++ b/scripts/qapi-visit.py
218e99
@@ -176,6 +176,49 @@ void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **e
218e99
 ''',
218e99
                  name=name)
218e99
 
218e99
+def generate_visit_anon_union(name, members):
218e99
+    ret = mcgen('''
218e99
+
218e99
+void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
218e99
+{
218e99
+    Error *err = NULL;
218e99
+
218e99
+    if (!error_is_set(errp)) {
218e99
+        visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
218e99
+        visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
218e99
+        switch ((*obj)->kind) {
218e99
+''',
218e99
+    name=name)
218e99
+
218e99
+    for key in members:
218e99
+        assert (members[key] in builtin_types
218e99
+            or find_struct(members[key])
218e99
+            or find_union(members[key])), "Invalid anonymous union member"
218e99
+
218e99
+        ret += mcgen('''
218e99
+        case %(abbrev)s_KIND_%(enum)s:
218e99
+            visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
218e99
+            break;
218e99
+''',
218e99
+                abbrev = de_camel_case(name).upper(),
218e99
+                enum = c_fun(de_camel_case(key),False).upper(),
218e99
+                c_type = type_name(members[key]),
218e99
+                c_name = c_fun(key))
218e99
+
218e99
+    ret += mcgen('''
218e99
+        default:
218e99
+            abort();
218e99
+        }
218e99
+        error_propagate(errp, err);
218e99
+        err = NULL;
218e99
+        visit_end_implicit_struct(m, &err);
218e99
+    }
218e99
+}
218e99
+''')
218e99
+
218e99
+    return ret
218e99
+
218e99
+
218e99
 def generate_visit_union(expr):
218e99
 
218e99
     name = expr['union']
218e99
@@ -184,6 +227,10 @@ def generate_visit_union(expr):
218e99
     base = expr.get('base')
218e99
     discriminator = expr.get('discriminator')
218e99
 
218e99
+    if discriminator == {}:
218e99
+        assert not base
218e99
+        return generate_visit_anon_union(name, members)
218e99
+
218e99
     ret = generate_visit_enum('%sKind' % name, members.keys())
218e99
 
218e99
     if base:
218e99
diff --git a/scripts/qapi.py b/scripts/qapi.py
218e99
index 3a54c7f..38c808e 100644
218e99
--- a/scripts/qapi.py
218e99
+++ b/scripts/qapi.py
218e99
@@ -17,6 +17,21 @@ builtin_types = [
218e99
     'uint8', 'uint16', 'uint32', 'uint64'
218e99
 ]
218e99
 
218e99
+builtin_type_qtypes = {
218e99
+    'str':      'QTYPE_QSTRING',
218e99
+    'int':      'QTYPE_QINT',
218e99
+    'number':   'QTYPE_QFLOAT',
218e99
+    'bool':     'QTYPE_QBOOL',
218e99
+    'int8':     'QTYPE_QINT',
218e99
+    'int16':    'QTYPE_QINT',
218e99
+    'int32':    'QTYPE_QINT',
218e99
+    'int64':    'QTYPE_QINT',
218e99
+    'uint8':    'QTYPE_QINT',
218e99
+    'uint16':   'QTYPE_QINT',
218e99
+    'uint32':   'QTYPE_QINT',
218e99
+    'uint64':   'QTYPE_QINT',
218e99
+}
218e99
+
218e99
 def tokenize(data):
218e99
     while len(data):
218e99
         ch = data[0]
218e99
-- 
218e99
1.7.1
218e99