From 7264f5c50cc1be0f1406e3ebb45aedcca02f603a Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 1 Dec 2015 22:20:47 -0700 Subject: [PATCH] qapi: Convert QType into QAPI built-in enum type What's more meta than using qapi to define qapi? :) Convert QType into a full-fledged[*] builtin qapi enum type, so that a subsequent patch can then use it as the discriminator type of qapi alternate types. Fortunately, the judicious use of 'prefix' in the qapi definition avoids churn to the spelling of the enum constants. To avoid circular definitions, we have to flip the order of inclusion between "qobject.h" vs. "qapi-types.h". Back in commit 28770e0, we had the latter include the former, so that we could use 'QObject *' for our implementation of 'any'. But that usage also works with only a forward declaration, whereas the definition of QObject requires QType to be a complete type. [*] The type has to be builtin, rather than declared in qapi/common.json, because we want to use it for alternates even when common.json is not included. But since it is the first builtin enum type, we have to add special cases to qapi-types and qapi-visit to only emit definitions once, even when two qapi files are being compiled into the same binary (the way we already handled builtin list types like 'intList'). We may need to revisit how multiple qapi files share common types, but that's a project for another day. Signed-off-by: Eric Blake Message-Id: <1449033659-25497-4-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster --- docs/qapi-code-gen.txt | 1 + include/qapi/qmp/qobject.h | 21 +++++---------------- include/qemu/typedefs.h | 1 + qobject/qobject.c | 4 ++-- scripts/qapi-types.py | 16 ++++++++++++---- scripts/qapi-visit.py | 11 +++++++++-- scripts/qapi.py | 6 ++++++ tests/qapi-schema/alternate-empty.out | 2 ++ tests/qapi-schema/comments.out | 2 ++ tests/qapi-schema/empty.out | 2 ++ tests/qapi-schema/event-case.out | 2 ++ tests/qapi-schema/flat-union-empty.out | 2 ++ tests/qapi-schema/ident-with-escape.out | 2 ++ tests/qapi-schema/include-relpath.out | 2 ++ tests/qapi-schema/include-repetition.out | 2 ++ tests/qapi-schema/include-simple.out | 2 ++ tests/qapi-schema/indented-expr.out | 2 ++ tests/qapi-schema/qapi-schema-test.out | 2 ++ tests/qapi-schema/union-clash-data.out | 2 ++ tests/qapi-schema/union-empty.out | 2 ++ 20 files changed, 62 insertions(+), 24 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 2becba95b9..79bf072695 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -160,6 +160,7 @@ The following types are predefined, and map to C as follows: accepts size suffixes bool bool JSON true or false any QObject * any JSON value + QType QType JSON string matching enum QType values === Includes === diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index c0f1e99bca..74459ae14b 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -34,23 +34,12 @@ #include #include +#include "qapi-types.h" -typedef enum { - QTYPE_NONE, /* sentinel value, no QObject has this type code */ - QTYPE_QNULL, - QTYPE_QINT, - QTYPE_QSTRING, - QTYPE_QDICT, - QTYPE_QLIST, - QTYPE_QFLOAT, - QTYPE_QBOOL, - QTYPE_MAX, -} QType; - -typedef struct QObject { +struct QObject { QType type; size_t refcnt; -} QObject; +}; /* Get the 'base' part of an object */ #define QOBJECT(obj) (&(obj)->base) @@ -66,7 +55,7 @@ typedef struct QObject { /* Initialize an object to default values */ static inline void qobject_init(QObject *obj, QType type) { - assert(QTYPE_NONE < type && type < QTYPE_MAX); + assert(QTYPE_NONE < type && type < QTYPE__MAX); obj->refcnt = 1; obj->type = type; } @@ -102,7 +91,7 @@ static inline void qobject_decref(QObject *obj) */ static inline QType qobject_type(const QObject *obj) { - assert(QTYPE_NONE < obj->type && obj->type < QTYPE_MAX); + assert(QTYPE_NONE < obj->type && obj->type < QTYPE__MAX); return obj->type; } diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 3eedcf4c8f..78fe6e86e3 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -80,6 +80,7 @@ typedef struct QEMUSGList QEMUSGList; typedef struct QEMUSizedBuffer QEMUSizedBuffer; typedef struct QEMUTimer QEMUTimer; typedef struct QEMUTimerListGroup QEMUTimerListGroup; +typedef struct QObject QObject; typedef struct RAMBlock RAMBlock; typedef struct Range Range; typedef struct SerialState SerialState; diff --git a/qobject/qobject.c b/qobject/qobject.c index 1df315ab01..a3ef14eb55 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -15,7 +15,7 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" -static void (*qdestroy[QTYPE_MAX])(QObject *) = { +static void (*qdestroy[QTYPE__MAX])(QObject *) = { [QTYPE_NONE] = NULL, /* No such object exists */ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ [QTYPE_QINT] = qint_destroy_obj, @@ -29,6 +29,6 @@ static void (*qdestroy[QTYPE_MAX])(QObject *) = { void qobject_destroy(QObject *obj) { assert(!obj->refcnt); - assert(QTYPE_QNULL < obj->type && obj->type < QTYPE_MAX); + assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); qdestroy[obj->type](obj); } diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 2f2f7dfd80..2071846250 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -112,7 +112,7 @@ extern const int %(c_name)s_qtypes[]; def gen_alternate_qtypes(name, variants): ret = mcgen(''' -const int %(c_name)s_qtypes[QTYPE_MAX] = { +const int %(c_name)s_qtypes[QTYPE__MAX] = { ''', c_name=c_name(name)) @@ -233,8 +233,15 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self.defn += gen_type_cleanup(name) def visit_enum_type(self, name, info, values, prefix): - self._fwdecl += gen_enum(name, values, prefix) - self._fwdefn += gen_enum_lookup(name, values, prefix) + # Special case for our lone builtin enum type + # TODO use something cleaner than existence of info + if not info: + self._btin += gen_enum(name, values, prefix) + if do_builtins: + self.defn += gen_enum_lookup(name, values, prefix) + else: + self._fwdecl += gen_enum(name, values, prefix) + self._fwdefn += gen_enum_lookup(name, values, prefix) def visit_array_type(self, name, info, element_type): if isinstance(element_type, QAPISchemaBuiltinType): @@ -316,10 +323,11 @@ fdef.write(mcgen(''' ''', prefix=prefix)) +# To avoid circular headers, use only typedefs.h here, not qobject.h fdecl.write(mcgen(''' #include #include -#include "qapi/qmp/qobject.h" +#include "qemu/typedefs.h" ''')) schema = QAPISchema(input_file) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 94cd11335e..7ceda18bdb 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -347,8 +347,15 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): isinstance(entity, QAPISchemaObjectType)) def visit_enum_type(self, name, info, values, prefix): - self.decl += gen_visit_decl(name, scalar=True) - self.defn += gen_visit_enum(name) + # Special case for our lone builtin enum type + # TODO use something cleaner than existence of info + if not info: + self._btin += gen_visit_decl(name, scalar=True) + if do_builtins: + self.defn += gen_visit_enum(name) + else: + self.decl += gen_visit_decl(name, scalar=True) + self.defn += gen_visit_enum(name) def visit_array_type(self, name, info, element_type): decl = gen_visit_decl(name) diff --git a/scripts/qapi.py b/scripts/qapi.py index b336fbd57c..c9e4ad2d9e 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -34,6 +34,7 @@ builtin_types = { 'uint64': 'QTYPE_QINT', 'size': 'QTYPE_QINT', 'any': None, # any QType possible, actually + 'QType': 'QTYPE_QSTRING', } # Whitelist of commands allowed to return a non-dictionary @@ -1244,6 +1245,11 @@ class QAPISchema(object): self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None, [], None) self._def_entity(self.the_empty_object_type) + self._def_entity(QAPISchemaEnumType('QType', None, + ['none', 'qnull', 'qint', + 'qstring', 'qdict', 'qlist', + 'qfloat', 'qbool'], + 'QTYPE')) def _make_implicit_enum_type(self, name, info, values): name = name + 'Kind' # Use namespace reserved by add_name() diff --git a/tests/qapi-schema/alternate-empty.out b/tests/qapi-schema/alternate-empty.out index 0f153b6f60..02b9876e76 100644 --- a/tests/qapi-schema/alternate-empty.out +++ b/tests/qapi-schema/alternate-empty.out @@ -2,3 +2,5 @@ object :empty alternate Alt case i: int enum AltKind ['i'] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out index 9e2c656fa0..97be601897 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1,2 +1,4 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index 272b1616f4..6522940dc4 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -1 +1,3 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out index cdfd264f9c..6350d6497b 100644 --- a/tests/qapi-schema/event-case.out +++ b/tests/qapi-schema/event-case.out @@ -1,2 +1,4 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE event oops None diff --git a/tests/qapi-schema/flat-union-empty.out b/tests/qapi-schema/flat-union-empty.out index 0e0665af3b..eade2d5ac6 100644 --- a/tests/qapi-schema/flat-union-empty.out +++ b/tests/qapi-schema/flat-union-empty.out @@ -2,6 +2,8 @@ object :empty object Base member type: Empty optional=False enum Empty [] +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE object Union base Base tag type diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index f4542b12d8..453e0b2adb 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -1,5 +1,7 @@ object :empty object :obj-fooA-arg member bar1: str optional=False +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE command fooA :obj-fooA-arg -> None gen=True success_response=True diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out index 9e2c656fa0..97be601897 100644 --- a/tests/qapi-schema/include-relpath.out +++ b/tests/qapi-schema/include-relpath.out @@ -1,2 +1,4 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 9e2c656fa0..97be601897 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1,2 +1,4 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 9e2c656fa0..97be601897 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1,2 +1,4 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 226d300798..ce37ff572b 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,4 +1,6 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE command eins None -> None gen=True success_response=True command zwei None -> None diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 0724a9f932..b87cfba599 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -101,6 +101,8 @@ object NestedEnumsOne member enum4: EnumOne optional=True enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE object TestStruct member integer: int optional=False member boolean: bool optional=False diff --git a/tests/qapi-schema/union-clash-data.out b/tests/qapi-schema/union-clash-data.out index cea8551c4c..f5752f4595 100644 --- a/tests/qapi-schema/union-clash-data.out +++ b/tests/qapi-schema/union-clash-data.out @@ -1,6 +1,8 @@ object :empty object :obj-int-wrapper member data: int optional=False +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE object TestUnion member type: TestUnionKind optional=False case data: :obj-int-wrapper diff --git a/tests/qapi-schema/union-empty.out b/tests/qapi-schema/union-empty.out index 9c89fd10ba..bdf17e5b29 100644 --- a/tests/qapi-schema/union-empty.out +++ b/tests/qapi-schema/union-empty.out @@ -1,4 +1,6 @@ object :empty +enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] + prefix QTYPE object Union member type: UnionKind optional=False enum UnionKind []