yeahuh / rpms / qemu-kvm

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