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 <eblake@redhat.com>
Message-Id: <1449033659-25497-4-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Eric Blake 2015-12-01 22:20:47 -07:00 committed by Markus Armbruster
parent 1310a3d3bd
commit 7264f5c50c
20 changed files with 62 additions and 24 deletions

View file

@ -160,6 +160,7 @@ The following types are predefined, and map to C as follows:
accepts size suffixes accepts size suffixes
bool bool JSON true or false bool bool JSON true or false
any QObject * any JSON value any QObject * any JSON value
QType QType JSON string matching enum QType values
=== Includes === === Includes ===

View file

@ -34,23 +34,12 @@
#include <stddef.h> #include <stddef.h>
#include <assert.h> #include <assert.h>
#include "qapi-types.h"
typedef enum { struct QObject {
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 {
QType type; QType type;
size_t refcnt; size_t refcnt;
} QObject; };
/* Get the 'base' part of an object */ /* Get the 'base' part of an object */
#define QOBJECT(obj) (&(obj)->base) #define QOBJECT(obj) (&(obj)->base)
@ -66,7 +55,7 @@ typedef struct QObject {
/* Initialize an object to default values */ /* Initialize an object to default values */
static inline void qobject_init(QObject *obj, QType type) 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->refcnt = 1;
obj->type = type; obj->type = type;
} }
@ -102,7 +91,7 @@ static inline void qobject_decref(QObject *obj)
*/ */
static inline QType qobject_type(const 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; return obj->type;
} }

View file

@ -80,6 +80,7 @@ typedef struct QEMUSGList QEMUSGList;
typedef struct QEMUSizedBuffer QEMUSizedBuffer; typedef struct QEMUSizedBuffer QEMUSizedBuffer;
typedef struct QEMUTimer QEMUTimer; typedef struct QEMUTimer QEMUTimer;
typedef struct QEMUTimerListGroup QEMUTimerListGroup; typedef struct QEMUTimerListGroup QEMUTimerListGroup;
typedef struct QObject QObject;
typedef struct RAMBlock RAMBlock; typedef struct RAMBlock RAMBlock;
typedef struct Range Range; typedef struct Range Range;
typedef struct SerialState SerialState; typedef struct SerialState SerialState;

View file

@ -15,7 +15,7 @@
#include "qapi/qmp/qlist.h" #include "qapi/qmp/qlist.h"
#include "qapi/qmp/qstring.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_NONE] = NULL, /* No such object exists */
[QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */
[QTYPE_QINT] = qint_destroy_obj, [QTYPE_QINT] = qint_destroy_obj,
@ -29,6 +29,6 @@ static void (*qdestroy[QTYPE_MAX])(QObject *) = {
void qobject_destroy(QObject *obj) void qobject_destroy(QObject *obj)
{ {
assert(!obj->refcnt); 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); qdestroy[obj->type](obj);
} }

View file

@ -112,7 +112,7 @@ extern const int %(c_name)s_qtypes[];
def gen_alternate_qtypes(name, variants): def gen_alternate_qtypes(name, variants):
ret = mcgen(''' ret = mcgen('''
const int %(c_name)s_qtypes[QTYPE_MAX] = { const int %(c_name)s_qtypes[QTYPE__MAX] = {
''', ''',
c_name=c_name(name)) c_name=c_name(name))
@ -233,8 +233,15 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
self.defn += gen_type_cleanup(name) self.defn += gen_type_cleanup(name)
def visit_enum_type(self, name, info, values, prefix): def visit_enum_type(self, name, info, values, prefix):
self._fwdecl += gen_enum(name, values, prefix) # Special case for our lone builtin enum type
self._fwdefn += gen_enum_lookup(name, values, prefix) # 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): def visit_array_type(self, name, info, element_type):
if isinstance(element_type, QAPISchemaBuiltinType): if isinstance(element_type, QAPISchemaBuiltinType):
@ -316,10 +323,11 @@ fdef.write(mcgen('''
''', ''',
prefix=prefix)) prefix=prefix))
# To avoid circular headers, use only typedefs.h here, not qobject.h
fdecl.write(mcgen(''' fdecl.write(mcgen('''
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "qapi/qmp/qobject.h" #include "qemu/typedefs.h"
''')) '''))
schema = QAPISchema(input_file) schema = QAPISchema(input_file)

View file

@ -347,8 +347,15 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
isinstance(entity, QAPISchemaObjectType)) isinstance(entity, QAPISchemaObjectType))
def visit_enum_type(self, name, info, values, prefix): def visit_enum_type(self, name, info, values, prefix):
self.decl += gen_visit_decl(name, scalar=True) # Special case for our lone builtin enum type
self.defn += gen_visit_enum(name) # 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): def visit_array_type(self, name, info, element_type):
decl = gen_visit_decl(name) decl = gen_visit_decl(name)

View file

@ -34,6 +34,7 @@ builtin_types = {
'uint64': 'QTYPE_QINT', 'uint64': 'QTYPE_QINT',
'size': 'QTYPE_QINT', 'size': 'QTYPE_QINT',
'any': None, # any QType possible, actually 'any': None, # any QType possible, actually
'QType': 'QTYPE_QSTRING',
} }
# Whitelist of commands allowed to return a non-dictionary # 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, self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
[], None) [], None)
self._def_entity(self.the_empty_object_type) 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): def _make_implicit_enum_type(self, name, info, values):
name = name + 'Kind' # Use namespace reserved by add_name() name = name + 'Kind' # Use namespace reserved by add_name()

View file

@ -2,3 +2,5 @@ object :empty
alternate Alt alternate Alt
case i: int case i: int
enum AltKind ['i'] enum AltKind ['i']
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE

View file

@ -1,2 +1,4 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
enum Status ['good', 'bad', 'ugly'] enum Status ['good', 'bad', 'ugly']

View file

@ -1 +1,3 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE

View file

@ -1,2 +1,4 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
event oops None event oops None

View file

@ -2,6 +2,8 @@ object :empty
object Base object Base
member type: Empty optional=False member type: Empty optional=False
enum Empty [] enum Empty []
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
object Union object Union
base Base base Base
tag type tag type

View file

@ -1,5 +1,7 @@
object :empty object :empty
object :obj-fooA-arg object :obj-fooA-arg
member bar1: str optional=False member bar1: str optional=False
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
command fooA :obj-fooA-arg -> None command fooA :obj-fooA-arg -> None
gen=True success_response=True gen=True success_response=True

View file

@ -1,2 +1,4 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
enum Status ['good', 'bad', 'ugly'] enum Status ['good', 'bad', 'ugly']

View file

@ -1,2 +1,4 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
enum Status ['good', 'bad', 'ugly'] enum Status ['good', 'bad', 'ugly']

View file

@ -1,2 +1,4 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
enum Status ['good', 'bad', 'ugly'] enum Status ['good', 'bad', 'ugly']

View file

@ -1,4 +1,6 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
command eins None -> None command eins None -> None
gen=True success_response=True gen=True success_response=True
command zwei None -> None command zwei None -> None

View file

@ -101,6 +101,8 @@ object NestedEnumsOne
member enum4: EnumOne optional=True member enum4: EnumOne optional=True
enum QEnumTwo ['value1', 'value2'] enum QEnumTwo ['value1', 'value2']
prefix QENUM_TWO prefix QENUM_TWO
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
object TestStruct object TestStruct
member integer: int optional=False member integer: int optional=False
member boolean: bool optional=False member boolean: bool optional=False

View file

@ -1,6 +1,8 @@
object :empty object :empty
object :obj-int-wrapper object :obj-int-wrapper
member data: int optional=False member data: int optional=False
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
object TestUnion object TestUnion
member type: TestUnionKind optional=False member type: TestUnionKind optional=False
case data: :obj-int-wrapper case data: :obj-int-wrapper

View file

@ -1,4 +1,6 @@
object :empty object :empty
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE
object Union object Union
member type: UnionKind optional=False member type: UnionKind optional=False
enum UnionKind [] enum UnionKind []