QAPI patches for 2016-02-19

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJWxuoKAAoJEDhwtADrkYZT8ZkP/RqSevpfXSsLwmGa10olkGYd
 9++3Sl2vu4X5iFFYBYHj9eIzzt5T9CCBRQGvu2N0baO3/NVFVmrXHrRXu5FHn3AV
 /dqnF5Cp8UMvBh4q/+z0IiN67mHlUMxZex/tjfL+6YeSi6TYEMGYxIq0cUv9HpPS
 jJtTNWgR/erNUMYg0IRePJ/Gounfs3nrWKy76Z2Ni7NABOACN5QFWMQWIS+P6+4j
 zJn92FziNh0qRckc/yYkd1x+p3IaCoALw38ovOZZE76EgBFEtkb2Ccl/4zQyxUkE
 ItjknuZFnjHdZCi2mngYFqqGEPspxYddtKoDKI6J6AoiZ4wXkkpfW44JrRLP8yIp
 3ZPYutH7wyHsh0t+s3rML/hwxGnYuDvf0XTIgmh9ZfGGSLSw93KbujONqZ6OFqNC
 FFPSgdhtvyB/TETp+TYu469PBtlKCG/7cT4CZM64BxaYUxHPl1e1xb2TfVDdE4vK
 rurQwAg2woHU8BqUYdCfMdeDJOhtI2FyBK8F5jSPq6Bj+MRBWgYSRF1BQh0xJTay
 WU7Tkn3mnm8CaB55+sOQnnwtGoe77NhBj341g5uTSwsca4c1bpb7WFYZ5iQU/lvm
 58ILjHs4mV09VkSs5QnNlfPLL4rz0gJtNWXT86T5VUveDDr0bj6qe1Ek2cO7fQ9z
 ztSODnQeM5nfvpH06cVe
 =PNgf
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2016-02-19' into staging

QAPI patches for 2016-02-19

# gpg: Signature made Fri 19 Feb 2016 10:10:18 GMT using RSA key ID EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"

* remotes/armbru/tags/pull-qapi-2016-02-19:
  qapi: Change visit_start_implicit_struct to visit_start_alternate
  qapi: Don't box branches of flat unions
  qapi: Don't box struct branch of alternate
  qapi-visit: Use common idiom in gen_visit_fields_decl()
  qapi: Emit structs used as variants in topological order
  qapi: Adjust layout of FooList types
  qapi-visit: Less indirection in visit_type_Foo_fields()
  qapi-visit: Unify struct and union visit
  qapi: Visit variants in visit_type_FOO_fields()
  qapi-visit: Simplify how we visit common union members
  qapi: Add tests of complex objects within alternate
  qapi: Forbid 'any' inside an alternate
  qapi: Forbid empty unions and useless alternates
  qapi: Simplify excess input reporting in input visitors
  qapi-visit: Honor prefix of discriminator enum

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-02-19 14:18:21 +00:00
commit 5cfffc30de
36 changed files with 360 additions and 375 deletions

18
cpus.c
View File

@ -1568,28 +1568,22 @@ CpuInfoList *qmp_query_cpus(Error **errp)
info->value->thread_id = cpu->thread_id; info->value->thread_id = cpu->thread_id;
#if defined(TARGET_I386) #if defined(TARGET_I386)
info->value->arch = CPU_INFO_ARCH_X86; info->value->arch = CPU_INFO_ARCH_X86;
info->value->u.x86 = g_new0(CpuInfoX86, 1); info->value->u.x86.pc = env->eip + env->segs[R_CS].base;
info->value->u.x86->pc = env->eip + env->segs[R_CS].base;
#elif defined(TARGET_PPC) #elif defined(TARGET_PPC)
info->value->arch = CPU_INFO_ARCH_PPC; info->value->arch = CPU_INFO_ARCH_PPC;
info->value->u.ppc = g_new0(CpuInfoPPC, 1); info->value->u.ppc.nip = env->nip;
info->value->u.ppc->nip = env->nip;
#elif defined(TARGET_SPARC) #elif defined(TARGET_SPARC)
info->value->arch = CPU_INFO_ARCH_SPARC; info->value->arch = CPU_INFO_ARCH_SPARC;
info->value->u.q_sparc = g_new0(CpuInfoSPARC, 1); info->value->u.q_sparc.pc = env->pc;
info->value->u.q_sparc->pc = env->pc; info->value->u.q_sparc.npc = env->npc;
info->value->u.q_sparc->npc = env->npc;
#elif defined(TARGET_MIPS) #elif defined(TARGET_MIPS)
info->value->arch = CPU_INFO_ARCH_MIPS; info->value->arch = CPU_INFO_ARCH_MIPS;
info->value->u.q_mips = g_new0(CpuInfoMIPS, 1); info->value->u.q_mips.PC = env->active_tc.PC;
info->value->u.q_mips->PC = env->active_tc.PC;
#elif defined(TARGET_TRICORE) #elif defined(TARGET_TRICORE)
info->value->arch = CPU_INFO_ARCH_TRICORE; info->value->arch = CPU_INFO_ARCH_TRICORE;
info->value->u.tricore = g_new0(CpuInfoTricore, 1); info->value->u.tricore.PC = env->PC;
info->value->u.tricore->PC = env->PC;
#else #else
info->value->arch = CPU_INFO_ARCH_OTHER; info->value->arch = CPU_INFO_ARCH_OTHER;
info->value->u.other = g_new0(CpuInfoOther, 1);
#endif #endif
/* XXX: waiting for the qapi to support GSList */ /* XXX: waiting for the qapi to support GSList */

View File

@ -187,11 +187,11 @@ prevent incomplete include files.
Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME } Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME }
A struct is a dictionary containing a single 'data' key whose A struct is a dictionary containing a single 'data' key whose value is
value is a dictionary. This corresponds to a struct in C or an Object a dictionary; the dictionary may be empty. This corresponds to a
in JSON. Each value of the 'data' dictionary must be the name of a struct in C or an Object in JSON. Each value of the 'data' dictionary
type, or a one-element array containing a type name. An example of a must be the name of a type, or a one-element array containing a type
struct is: name. An example of a struct is:
{ 'struct': 'MyType', { 'struct': 'MyType',
'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } } 'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } }
@ -288,9 +288,10 @@ or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
Union types are used to let the user choose between several different Union types are used to let the user choose between several different
variants for an object. There are two flavors: simple (no variants for an object. There are two flavors: simple (no
discriminator or base), flat (both discriminator and base). A union discriminator or base), and flat (both discriminator and base). A union
type is defined using a data dictionary as explained in the following type is defined using a data dictionary as explained in the following
paragraphs. paragraphs. The data dictionary for either type of union must not
be empty.
A simple union type defines a mapping from automatic discriminator A simple union type defines a mapping from automatic discriminator
values to data types like in this example: values to data types like in this example:

12
hmp.c
View File

@ -314,22 +314,22 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
switch (cpu->value->arch) { switch (cpu->value->arch) {
case CPU_INFO_ARCH_X86: case CPU_INFO_ARCH_X86:
monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86->pc); monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86.pc);
break; break;
case CPU_INFO_ARCH_PPC: case CPU_INFO_ARCH_PPC:
monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc->nip); monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc.nip);
break; break;
case CPU_INFO_ARCH_SPARC: case CPU_INFO_ARCH_SPARC:
monitor_printf(mon, " pc=0x%016" PRIx64, monitor_printf(mon, " pc=0x%016" PRIx64,
cpu->value->u.q_sparc->pc); cpu->value->u.q_sparc.pc);
monitor_printf(mon, " npc=0x%016" PRIx64, monitor_printf(mon, " npc=0x%016" PRIx64,
cpu->value->u.q_sparc->npc); cpu->value->u.q_sparc.npc);
break; break;
case CPU_INFO_ARCH_MIPS: case CPU_INFO_ARCH_MIPS:
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips->PC); monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips.PC);
break; break;
case CPU_INFO_ARCH_TRICORE: case CPU_INFO_ARCH_TRICORE:
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore->PC); monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore.PC);
break; break;
default: default:
break; break;

View File

@ -22,22 +22,23 @@ struct Visitor
size_t size, Error **errp); size_t size, Error **errp);
void (*end_struct)(Visitor *v, Error **errp); void (*end_struct)(Visitor *v, Error **errp);
void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
Error **errp);
/* May be NULL */
void (*end_implicit_struct)(Visitor *v);
void (*start_list)(Visitor *v, const char *name, Error **errp); void (*start_list)(Visitor *v, const char *name, Error **errp);
/* Must be set */ /* Must be set */
GenericList *(*next_list)(Visitor *v, GenericList **list); GenericList *(*next_list)(Visitor *v, GenericList **list, size_t size);
/* Must be set */ /* Must be set */
void (*end_list)(Visitor *v); void (*end_list)(Visitor *v);
/* Optional, needed for input and dealloc visitors. */
void (*start_alternate)(Visitor *v, const char *name,
GenericAlternate **obj, size_t size,
bool promote_int, Error **errp);
/* Optional, needed for dealloc visitor. */
void (*end_alternate)(Visitor *v);
/* Must be set. */
void (*type_enum)(Visitor *v, const char *name, int *obj, void (*type_enum)(Visitor *v, const char *name, int *obj,
const char *const strings[], Error **errp); const char *const strings[], Error **errp);
/* May be NULL; only needed for input visitors. */
void (*get_next_type)(Visitor *v, const char *name, QType *type,
bool promote_int, Error **errp);
/* Must be set. */ /* Must be set. */
void (*type_int64)(Visitor *v, const char *name, int64_t *obj, void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
@ -58,8 +59,6 @@ struct Visitor
/* May be NULL; most useful for input visitors. */ /* May be NULL; most useful for input visitors. */
void (*optional)(Visitor *v, const char *name, bool *present); void (*optional)(Visitor *v, const char *name, bool *present);
bool (*start_union)(Visitor *v, bool data_present, Error **errp);
}; };
void input_type_enum(Visitor *v, const char *name, int *obj, void input_type_enum(Visitor *v, const char *name, int *obj,

View File

@ -19,26 +19,60 @@
#include "qapi/error.h" #include "qapi/error.h"
#include <stdlib.h> #include <stdlib.h>
typedef struct GenericList /* This struct is layout-compatible with all other *List structs
{ * created by the qapi generator. It is used as a typical
union { * singly-linked list. */
void *value; typedef struct GenericList {
uint64_t padding;
};
struct GenericList *next; struct GenericList *next;
char padding[];
} GenericList; } GenericList;
/* This struct is layout-compatible with all Alternate types
* created by the qapi generator. */
typedef struct GenericAlternate {
QType type;
char padding[];
} GenericAlternate;
void visit_start_struct(Visitor *v, const char *name, void **obj, void visit_start_struct(Visitor *v, const char *name, void **obj,
size_t size, Error **errp); size_t size, Error **errp);
void visit_end_struct(Visitor *v, Error **errp); void visit_end_struct(Visitor *v, Error **errp);
void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
Error **errp);
void visit_end_implicit_struct(Visitor *v);
void visit_start_list(Visitor *v, const char *name, Error **errp); void visit_start_list(Visitor *v, const char *name, Error **errp);
GenericList *visit_next_list(Visitor *v, GenericList **list); GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size);
void visit_end_list(Visitor *v); void visit_end_list(Visitor *v);
/*
* Start the visit of an alternate @obj with the given @size.
*
* @name specifies the relationship to the containing struct (ignored
* for a top level visit, the name of the key if this alternate is
* part of an object, or NULL if this alternate is part of a list).
*
* @obj must not be NULL. Input visitors will allocate @obj and
* determine the qtype of the next thing to be visited, stored in
* (*@obj)->type. Other visitors will leave @obj unchanged.
*
* If @promote_int, treat integers as QTYPE_FLOAT.
*
* If successful, this must be paired with visit_end_alternate(), even
* if visiting the contents of the alternate fails.
*/
void visit_start_alternate(Visitor *v, const char *name,
GenericAlternate **obj, size_t size,
bool promote_int, Error **errp);
/*
* Finish visiting an alternate type.
*
* Must be called after a successful visit_start_alternate(), even if
* an error occurred in the meantime.
*
* TODO: Should all the visit_end_* interfaces take obj parameter, so
* that dealloc visitor need not track what was passed in visit_start?
*/
void visit_end_alternate(Visitor *v);
/** /**
* Check if an optional member @name of an object needs visiting. * Check if an optional member @name of an object needs visiting.
* For input visitors, set *@present according to whether the * For input visitors, set *@present according to whether the
@ -47,14 +81,6 @@ void visit_end_list(Visitor *v);
*/ */
bool visit_optional(Visitor *v, const char *name, bool *present); bool visit_optional(Visitor *v, const char *name, bool *present);
/**
* Determine the qtype of the item @name in the current object visit.
* For input visitors, set *@type to the correct qtype of a qapi
* alternate type; for other visitors, leave *@type unchanged.
* If @promote_int, treat integers as QTYPE_FLOAT.
*/
void visit_get_next_type(Visitor *v, const char *name, QType *type,
bool promote_int, Error **errp);
void visit_type_enum(Visitor *v, const char *name, int *obj, void visit_type_enum(Visitor *v, const char *name, int *obj,
const char *const strings[], Error **errp); const char *const strings[], Error **errp);
void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp); void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
@ -80,6 +106,5 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
void visit_type_number(Visitor *v, const char *name, double *obj, void visit_type_number(Visitor *v, const char *name, double *obj,
Error **errp); Error **errp);
void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp); void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
bool visit_start_union(Visitor *v, bool data_present, Error **errp);
#endif #endif

View File

@ -157,17 +157,11 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
} }
static gboolean
ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
{
return TRUE;
}
static void static void
opts_end_struct(Visitor *v, Error **errp) opts_end_struct(Visitor *v, Error **errp)
{ {
OptsVisitor *ov = to_ov(v); OptsVisitor *ov = to_ov(v);
GHashTableIter iter;
GQueue *any; GQueue *any;
if (--ov->depth > 0) { if (--ov->depth > 0) {
@ -175,8 +169,8 @@ opts_end_struct(Visitor *v, Error **errp)
} }
/* we should have processed all (distinct) QemuOpt instances */ /* we should have processed all (distinct) QemuOpt instances */
any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL); g_hash_table_iter_init(&iter, ov->unprocessed_opts);
if (any) { if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
const QemuOpt *first; const QemuOpt *first;
first = g_queue_peek_head(any); first = g_queue_peek_head(any);
@ -221,7 +215,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
static GenericList * static GenericList *
opts_next_list(Visitor *v, GenericList **list) opts_next_list(Visitor *v, GenericList **list, size_t size)
{ {
OptsVisitor *ov = to_ov(v); OptsVisitor *ov = to_ov(v);
GenericList **link; GenericList **link;
@ -264,7 +258,7 @@ opts_next_list(Visitor *v, GenericList **list)
abort(); abort();
} }
*link = g_malloc0(sizeof **link); *link = g_malloc0(size);
return *link; return *link;
} }

View File

@ -76,16 +76,15 @@ static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
} }
} }
static void qapi_dealloc_start_implicit_struct(Visitor *v, static void qapi_dealloc_start_alternate(Visitor *v, const char *name,
void **obj, GenericAlternate **obj, size_t size,
size_t size, bool promote_int, Error **errp)
Error **errp)
{ {
QapiDeallocVisitor *qov = to_qov(v); QapiDeallocVisitor *qov = to_qov(v);
qapi_dealloc_push(qov, obj); qapi_dealloc_push(qov, obj);
} }
static void qapi_dealloc_end_implicit_struct(Visitor *v) static void qapi_dealloc_end_alternate(Visitor *v)
{ {
QapiDeallocVisitor *qov = to_qov(v); QapiDeallocVisitor *qov = to_qov(v);
void **obj = qapi_dealloc_pop(qov); void **obj = qapi_dealloc_pop(qov);
@ -100,7 +99,8 @@ static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp)
qapi_dealloc_push(qov, NULL); qapi_dealloc_push(qov, NULL);
} }
static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp) static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp,
size_t size)
{ {
GenericList *list = *listp; GenericList *list = *listp;
QapiDeallocVisitor *qov = to_qov(v); QapiDeallocVisitor *qov = to_qov(v);
@ -168,31 +168,6 @@ static void qapi_dealloc_type_enum(Visitor *v, const char *name, int *obj,
{ {
} }
/* If there's no data present, the dealloc visitor has nothing to free.
* Thus, indicate to visitor code that the subsequent union fields can
* be skipped. This is not an error condition, since the cleanup of the
* rest of an object can continue unhindered, so leave errp unset in
* these cases.
*
* NOTE: In cases where we're attempting to deallocate an object that
* may have missing fields, the field indicating the union type may
* be missing. In such a case, it's possible we don't have enough
* information to differentiate data_present == false from a case where
* data *is* present but happens to be a scalar with a value of 0.
* This is okay, since in the case of the dealloc visitor there's no
* work that needs to done in either situation.
*
* The current inability in QAPI code to more thoroughly verify a union
* type in such cases will likely need to be addressed if we wish to
* implement this interface for other types of visitors in the future,
* however.
*/
static bool qapi_dealloc_start_union(Visitor *v, bool data_present,
Error **errp)
{
return data_present;
}
Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v) Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
{ {
return &v->visitor; return &v->visitor;
@ -211,8 +186,8 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
v->visitor.start_struct = qapi_dealloc_start_struct; v->visitor.start_struct = qapi_dealloc_start_struct;
v->visitor.end_struct = qapi_dealloc_end_struct; v->visitor.end_struct = qapi_dealloc_end_struct;
v->visitor.start_implicit_struct = qapi_dealloc_start_implicit_struct; v->visitor.start_alternate = qapi_dealloc_start_alternate;
v->visitor.end_implicit_struct = qapi_dealloc_end_implicit_struct; v->visitor.end_alternate = qapi_dealloc_end_alternate;
v->visitor.start_list = qapi_dealloc_start_list; v->visitor.start_list = qapi_dealloc_start_list;
v->visitor.next_list = qapi_dealloc_next_list; v->visitor.next_list = qapi_dealloc_next_list;
v->visitor.end_list = qapi_dealloc_end_list; v->visitor.end_list = qapi_dealloc_end_list;
@ -223,7 +198,6 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
v->visitor.type_str = qapi_dealloc_type_str; v->visitor.type_str = qapi_dealloc_type_str;
v->visitor.type_number = qapi_dealloc_type_number; v->visitor.type_number = qapi_dealloc_type_number;
v->visitor.type_any = qapi_dealloc_type_anything; v->visitor.type_any = qapi_dealloc_type_anything;
v->visitor.start_union = qapi_dealloc_start_union;
QTAILQ_INIT(&v->stack); QTAILQ_INIT(&v->stack);

View File

@ -30,29 +30,15 @@ void visit_end_struct(Visitor *v, Error **errp)
v->end_struct(v, errp); v->end_struct(v, errp);
} }
void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
Error **errp)
{
if (v->start_implicit_struct) {
v->start_implicit_struct(v, obj, size, errp);
}
}
void visit_end_implicit_struct(Visitor *v)
{
if (v->end_implicit_struct) {
v->end_implicit_struct(v);
}
}
void visit_start_list(Visitor *v, const char *name, Error **errp) void visit_start_list(Visitor *v, const char *name, Error **errp)
{ {
v->start_list(v, name, errp); v->start_list(v, name, errp);
} }
GenericList *visit_next_list(Visitor *v, GenericList **list) GenericList *visit_next_list(Visitor *v, GenericList **list, size_t size)
{ {
return v->next_list(v, list); assert(list && size >= sizeof(GenericList));
return v->next_list(v, list, size);
} }
void visit_end_list(Visitor *v) void visit_end_list(Visitor *v)
@ -60,12 +46,21 @@ void visit_end_list(Visitor *v)
v->end_list(v); v->end_list(v);
} }
bool visit_start_union(Visitor *v, bool data_present, Error **errp) void visit_start_alternate(Visitor *v, const char *name,
GenericAlternate **obj, size_t size,
bool promote_int, Error **errp)
{ {
if (v->start_union) { assert(obj && size >= sizeof(GenericAlternate));
return v->start_union(v, data_present, errp); if (v->start_alternate) {
v->start_alternate(v, name, obj, size, promote_int, errp);
}
}
void visit_end_alternate(Visitor *v)
{
if (v->end_alternate) {
v->end_alternate(v);
} }
return true;
} }
bool visit_optional(Visitor *v, const char *name, bool *present) bool visit_optional(Visitor *v, const char *name, bool *present)
@ -76,14 +71,6 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
return *present; return *present;
} }
void visit_get_next_type(Visitor *v, const char *name, QType *type,
bool promote_int, Error **errp)
{
if (v->get_next_type) {
v->get_next_type(v, name, type, promote_int, errp);
}
}
void visit_type_enum(Visitor *v, const char *name, int *obj, void visit_type_enum(Visitor *v, const char *name, int *obj,
const char *const strings[], Error **errp) const char *const strings[], Error **errp)
{ {

View File

@ -90,12 +90,6 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
qiv->nb_stack++; qiv->nb_stack++;
} }
/** Only for qmp_input_pop. */
static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey)
{
*(const char **)user_pkey = (const char *)key;
return TRUE;
}
static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
{ {
@ -104,9 +98,11 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
if (qiv->strict) { if (qiv->strict) {
GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
if (top_ht) { if (top_ht) {
if (g_hash_table_size(top_ht)) { GHashTableIter iter;
const char *key; const char *key;
g_hash_table_find(top_ht, always_true, &key);
g_hash_table_iter_init(&iter, top_ht);
if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
} }
g_hash_table_unref(top_ht); g_hash_table_unref(top_ht);
@ -147,14 +143,6 @@ static void qmp_input_end_struct(Visitor *v, Error **errp)
qmp_input_pop(qiv, errp); qmp_input_pop(qiv, errp);
} }
static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
size_t size, Error **errp)
{
if (obj) {
*obj = g_malloc0(size);
}
}
static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
{ {
QmpInputVisitor *qiv = to_qiv(v); QmpInputVisitor *qiv = to_qiv(v);
@ -169,7 +157,8 @@ static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
qmp_input_push(qiv, qobj, errp); qmp_input_push(qiv, qobj, errp);
} }
static GenericList *qmp_input_next_list(Visitor *v, GenericList **list) static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
size_t size)
{ {
QmpInputVisitor *qiv = to_qiv(v); QmpInputVisitor *qiv = to_qiv(v);
GenericList *entry; GenericList *entry;
@ -188,7 +177,7 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList **list)
return NULL; return NULL;
} }
entry = g_malloc0(sizeof(*entry)); entry = g_malloc0(size);
if (first) { if (first) {
*list = entry; *list = entry;
} else { } else {
@ -205,19 +194,22 @@ static void qmp_input_end_list(Visitor *v)
qmp_input_pop(qiv, &error_abort); qmp_input_pop(qiv, &error_abort);
} }
static void qmp_input_get_next_type(Visitor *v, const char *name, QType *type, static void qmp_input_start_alternate(Visitor *v, const char *name,
GenericAlternate **obj, size_t size,
bool promote_int, Error **errp) bool promote_int, Error **errp)
{ {
QmpInputVisitor *qiv = to_qiv(v); QmpInputVisitor *qiv = to_qiv(v);
QObject *qobj = qmp_input_get_object(qiv, name, false); QObject *qobj = qmp_input_get_object(qiv, name, false);
if (!qobj) { if (!qobj) {
*obj = NULL;
error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null"); error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
return; return;
} }
*type = qobject_type(qobj); *obj = g_malloc0(size);
if (promote_int && *type == QTYPE_QINT) { (*obj)->type = qobject_type(qobj);
*type = QTYPE_QFLOAT; if (promote_int && (*obj)->type == QTYPE_QINT) {
(*obj)->type = QTYPE_QFLOAT;
} }
} }
@ -348,10 +340,10 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
v->visitor.start_struct = qmp_input_start_struct; v->visitor.start_struct = qmp_input_start_struct;
v->visitor.end_struct = qmp_input_end_struct; v->visitor.end_struct = qmp_input_end_struct;
v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
v->visitor.start_list = qmp_input_start_list; v->visitor.start_list = qmp_input_start_list;
v->visitor.next_list = qmp_input_next_list; v->visitor.next_list = qmp_input_next_list;
v->visitor.end_list = qmp_input_end_list; v->visitor.end_list = qmp_input_end_list;
v->visitor.start_alternate = qmp_input_start_alternate;
v->visitor.type_enum = input_type_enum; v->visitor.type_enum = input_type_enum;
v->visitor.type_int64 = qmp_input_type_int64; v->visitor.type_int64 = qmp_input_type_int64;
v->visitor.type_uint64 = qmp_input_type_uint64; v->visitor.type_uint64 = qmp_input_type_uint64;
@ -360,7 +352,6 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
v->visitor.type_number = qmp_input_type_number; v->visitor.type_number = qmp_input_type_number;
v->visitor.type_any = qmp_input_type_any; v->visitor.type_any = qmp_input_type_any;
v->visitor.optional = qmp_input_optional; v->visitor.optional = qmp_input_optional;
v->visitor.get_next_type = qmp_input_get_next_type;
qmp_input_push(v, obj, NULL); qmp_input_push(v, obj, NULL);
qobject_incref(obj); qobject_incref(obj);

View File

@ -126,7 +126,8 @@ static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
qmp_output_push(qov, list); qmp_output_push(qov, list);
} }
static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp) static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
size_t size)
{ {
GenericList *list = *listp; GenericList *list = *listp;
QmpOutputVisitor *qov = to_qov(v); QmpOutputVisitor *qov = to_qov(v);

View File

@ -139,7 +139,7 @@ start_list(Visitor *v, const char *name, Error **errp)
} }
} }
static GenericList *next_list(Visitor *v, GenericList **list) static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
{ {
StringInputVisitor *siv = to_siv(v); StringInputVisitor *siv = to_siv(v);
GenericList **link; GenericList **link;
@ -173,7 +173,7 @@ static GenericList *next_list(Visitor *v, GenericList **list)
link = &(*list)->next; link = &(*list)->next;
} }
*link = g_malloc0(sizeof **link); *link = g_malloc0(size);
return *link; return *link;
} }

View File

@ -276,7 +276,7 @@ start_list(Visitor *v, const char *name, Error **errp)
sov->head = true; sov->head = true;
} }
static GenericList *next_list(Visitor *v, GenericList **list) static GenericList *next_list(Visitor *v, GenericList **list, size_t size)
{ {
StringOutputVisitor *sov = to_sov(v); StringOutputVisitor *sov = to_sov(v);
GenericList *ret = NULL; GenericList *ret = NULL;

View File

@ -2,7 +2,7 @@
# QAPI types generator # QAPI types generator
# #
# Copyright IBM, Corp. 2011 # Copyright IBM, Corp. 2011
# Copyright (c) 2013-2015 Red Hat Inc. # Copyright (c) 2013-2016 Red Hat Inc.
# #
# Authors: # Authors:
# Anthony Liguori <aliguori@us.ibm.com> # Anthony Liguori <aliguori@us.ibm.com>
@ -14,6 +14,11 @@
from qapi import * from qapi import *
# variants must be emitted before their container; track what has already
# been output
objects_seen = set()
def gen_fwd_object_or_array(name): def gen_fwd_object_or_array(name):
return mcgen(''' return mcgen('''
@ -26,11 +31,8 @@ def gen_array(name, element_type):
return mcgen(''' return mcgen('''
struct %(c_name)s { struct %(c_name)s {
union {
%(c_type)s value;
uint64_t padding;
};
%(c_name)s *next; %(c_name)s *next;
%(c_type)s value;
}; };
''', ''',
c_name=c_name(name), c_type=element_type.c_type()) c_name=c_name(name), c_type=element_type.c_type())
@ -52,7 +54,19 @@ def gen_struct_fields(members):
def gen_object(name, base, members, variants): def gen_object(name, base, members, variants):
ret = mcgen(''' if name in objects_seen:
return ''
objects_seen.add(name)
ret = ''
if variants:
for v in variants.variants:
if (isinstance(v.type, QAPISchemaObjectType) and
not v.type.is_implicit()):
ret += gen_object(v.type.name, v.type.base,
v.type.local_members, v.type.variants)
ret += mcgen('''
struct %(c_name)s { struct %(c_name)s {
''', ''',
@ -118,11 +132,12 @@ def gen_variants(variants):
for var in variants.variants: for var in variants.variants:
# Ugly special case for simple union TODO get rid of it # Ugly special case for simple union TODO get rid of it
typ = var.simple_union_type() or var.type simple_union_type = var.simple_union_type()
typ = simple_union_type or var.type
ret += mcgen(''' ret += mcgen('''
%(c_type)s %(c_name)s; %(c_type)s %(c_name)s;
''', ''',
c_type=typ.c_type(), c_type=typ.c_type(is_unboxed=not simple_union_type),
c_name=c_name(var.name)) c_name=c_name(var.name))
ret += mcgen(''' ret += mcgen('''

View File

@ -15,10 +15,6 @@
from qapi import * from qapi import *
import re import re
# visit_type_FOO_implicit() is emitted as needed; track if it has already
# been output.
implicit_structs_seen = set()
# visit_type_FOO_fields() is always emitted; track if a forward declaration # visit_type_FOO_fields() is always emitted; track if a forward declaration
# or implementation has already been output. # or implementation has already been output.
struct_fields_seen = set() struct_fields_seen = set()
@ -35,52 +31,31 @@ def gen_visit_decl(name, scalar=False):
def gen_visit_fields_decl(typ): def gen_visit_fields_decl(typ):
ret = '' if typ.name in struct_fields_seen:
if typ.name not in struct_fields_seen:
ret += mcgen('''
static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
''',
c_type=typ.c_name())
struct_fields_seen.add(typ.name)
return ret
def gen_visit_implicit_struct(typ):
if typ in implicit_structs_seen:
return '' return ''
implicit_structs_seen.add(typ) struct_fields_seen.add(typ.name)
return mcgen('''
ret = gen_visit_fields_decl(typ) static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s *obj, Error **errp);
ret += mcgen('''
static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
{
Error *err = NULL;
visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
if (!err) {
visit_type_%(c_type)s_fields(v, obj, errp);
visit_end_implicit_struct(v);
}
error_propagate(errp, err);
}
''', ''',
c_type=typ.c_name()) c_type=typ.c_name())
return ret
def gen_visit_struct_fields(name, base, members): def gen_visit_struct_fields(name, base, members, variants):
ret = '' ret = ''
if base: if base:
ret += gen_visit_fields_decl(base) ret += gen_visit_fields_decl(base)
if variants:
for var in variants.variants:
# Ugly special case for simple union TODO get rid of it
if not var.simple_union_type():
ret += gen_visit_fields_decl(var.type)
struct_fields_seen.add(name) struct_fields_seen.add(name)
ret += mcgen(''' ret += mcgen('''
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp) static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s *obj, Error **errp)
{ {
Error *err = NULL; Error *err = NULL;
@ -89,57 +64,61 @@ def gen_visit_struct_fields(name, base, members):
if base: if base:
ret += mcgen(''' ret += mcgen('''
visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err); visit_type_%(c_type)s_fields(v, (%(c_type)s *)obj, &err);
''', ''',
c_type=base.c_name()) c_type=base.c_name())
ret += gen_err_check() ret += gen_err_check()
ret += gen_visit_fields(members, prefix='(*obj)->') ret += gen_visit_fields(members, prefix='obj->')
# 'goto out' produced for base, and by gen_visit_fields() for each member if variants:
if base or members:
ret += mcgen(''' ret += mcgen('''
switch (obj->%(c_name)s) {
out:
''')
ret += mcgen('''
error_propagate(errp, err);
}
''')
return ret
def gen_visit_struct(name, base, members):
ret = gen_visit_struct_fields(name, base, members)
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
# *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
# rather than leaving it non-NULL. As currently written, the caller must
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
ret += mcgen('''
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
{
Error *err = NULL;
visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
if (err) {
goto out;
}
if (!*obj) {
goto out_obj;
}
visit_type_%(c_name)s_fields(v, obj, &err);
error_propagate(errp, err);
err = NULL;
out_obj:
visit_end_struct(v, &err);
out:
error_propagate(errp, err);
}
''', ''',
c_name=c_name(name)) c_name=c_name(variants.tag_member.name))
for var in variants.variants:
# TODO ugly special case for simple union
simple_union_type = var.simple_union_type()
ret += mcgen('''
case %(case)s:
''',
case=c_enum_const(variants.tag_member.type.name,
var.name,
variants.tag_member.type.prefix))
if simple_union_type:
ret += mcgen('''
visit_type_%(c_type)s(v, "data", &obj->u.%(c_name)s, &err);
''',
c_type=simple_union_type.c_name(),
c_name=c_name(var.name))
else:
ret += mcgen('''
visit_type_%(c_type)s_fields(v, &obj->u.%(c_name)s, &err);
''',
c_type=var.type.c_name(),
c_name=c_name(var.name))
ret += mcgen('''
break;
''')
ret += mcgen('''
default:
abort();
}
''')
# 'goto out' produced for base, by gen_visit_fields() for each member,
# and if variants were present
if base or members or variants:
ret += mcgen('''
out:
''')
ret += mcgen('''
error_propagate(errp, err);
}
''')
return ret return ret
@ -161,7 +140,7 @@ def gen_visit_list(name, element_type):
} }
for (prev = (GenericList **)obj; for (prev = (GenericList **)obj;
!err && (i = visit_next_list(v, prev)) != NULL; !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
prev = &i) { prev = &i) {
%(c_name)s *native_i = (%(c_name)s *)i; %(c_name)s *native_i = (%(c_name)s *)i;
visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err); visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
@ -190,24 +169,24 @@ def gen_visit_enum(name):
def gen_visit_alternate(name, variants): def gen_visit_alternate(name, variants):
promote_int = 'true' promote_int = 'true'
ret = ''
for var in variants.variants: for var in variants.variants:
if var.type.alternate_qtype() == 'QTYPE_QINT': if var.type.alternate_qtype() == 'QTYPE_QINT':
promote_int = 'false' promote_int = 'false'
if isinstance(var.type, QAPISchemaObjectType):
ret += gen_visit_fields_decl(var.type)
ret = mcgen(''' ret += mcgen('''
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
{ {
Error *err = NULL; Error *err = NULL;
visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err); visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj),
%(promote_int)s, &err);
if (err) { if (err) {
goto out; goto out;
} }
visit_get_next_type(v, name, &(*obj)->type, %(promote_int)s, &err);
if (err) {
goto out_obj;
}
switch ((*obj)->type) { switch ((*obj)->type) {
''', ''',
c_name=c_name(name), promote_int=promote_int) c_name=c_name(name), promote_int=promote_int)
@ -215,20 +194,37 @@ def gen_visit_alternate(name, variants):
for var in variants.variants: for var in variants.variants:
ret += mcgen(''' ret += mcgen('''
case %(case)s: case %(case)s:
visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
break;
''', ''',
case=var.type.alternate_qtype(), case=var.type.alternate_qtype())
if isinstance(var.type, QAPISchemaObjectType):
ret += mcgen('''
visit_start_struct(v, name, NULL, 0, &err);
if (err) {
break;
}
visit_type_%(c_type)s_fields(v, &(*obj)->u.%(c_name)s, &err);
error_propagate(errp, err);
err = NULL;
visit_end_struct(v, &err);
''',
c_type=var.type.c_name(), c_type=var.type.c_name(),
c_name=c_name(var.name)) c_name=c_name(var.name))
else:
ret += mcgen('''
visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
''',
c_type=var.type.c_name(),
c_name=c_name(var.name))
ret += mcgen('''
break;
''')
ret += mcgen(''' ret += mcgen('''
default: default:
error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"%(name)s"); "%(name)s");
} }
out_obj: visit_end_alternate(v);
visit_end_implicit_struct(v);
out: out:
error_propagate(errp, err); error_propagate(errp, err);
} }
@ -238,17 +234,13 @@ def gen_visit_alternate(name, variants):
return ret return ret
def gen_visit_union(name, base, variants): def gen_visit_object(name, base, members, variants):
ret = '' ret = gen_visit_struct_fields(name, base, members, variants)
if base:
ret += gen_visit_fields_decl(base)
for var in variants.variants:
# Ugly special case for simple union TODO get rid of it
if not var.simple_union_type():
ret += gen_visit_implicit_struct(var.type)
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
# *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
# rather than leaving it non-NULL. As currently written, the caller must
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
ret += mcgen(''' ret += mcgen('''
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
@ -262,66 +254,16 @@ def gen_visit_union(name, base, variants):
if (!*obj) { if (!*obj) {
goto out_obj; goto out_obj;
} }
''', visit_type_%(c_name)s_fields(v, *obj, &err);
c_name=c_name(name))
if base:
ret += mcgen('''
visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
''',
c_name=base.c_name())
else:
ret += mcgen('''
visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
''',
c_type=variants.tag_member.type.c_name(),
c_name=c_name(variants.tag_member.name),
name=variants.tag_member.name)
ret += gen_err_check(label='out_obj')
ret += mcgen('''
if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
goto out_obj;
}
switch ((*obj)->%(c_name)s) {
''',
c_name=c_name(variants.tag_member.name))
for var in variants.variants:
# TODO ugly special case for simple union
simple_union_type = var.simple_union_type()
ret += mcgen('''
case %(case)s:
''',
case=c_enum_const(variants.tag_member.type.name,
var.name))
if simple_union_type:
ret += mcgen('''
visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
''',
c_type=simple_union_type.c_name(),
c_name=c_name(var.name))
else:
ret += mcgen('''
visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
''',
c_type=var.type.c_name(),
c_name=c_name(var.name))
ret += mcgen('''
break;
''')
ret += mcgen('''
default:
abort();
}
out_obj:
error_propagate(errp, err); error_propagate(errp, err);
err = NULL; err = NULL;
out_obj:
visit_end_struct(v, &err); visit_end_struct(v, &err);
out: out:
error_propagate(errp, err); error_propagate(errp, err);
} }
''') ''',
c_name=c_name(name))
return ret return ret
@ -375,14 +317,7 @@ def visit_array_type(self, name, info, element_type):
def visit_object_type(self, name, info, base, members, variants): def visit_object_type(self, name, info, base, members, variants):
self.decl += gen_visit_decl(name) self.decl += gen_visit_decl(name)
if variants: self.defn += gen_visit_object(name, base, members, variants)
if members:
# Members other than variants.tag_member not implemented
assert len(members) == 1
assert members[0] == variants.tag_member
self.defn += gen_visit_union(name, base, variants)
else:
self.defn += gen_visit_struct(name, base, members)
def visit_alternate_type(self, name, info, variants): def visit_alternate_type(self, name, info, variants):
self.decl += gen_visit_decl(name) self.decl += gen_visit_decl(name)

View File

@ -590,7 +590,10 @@ def check_union(expr, expr_info):
"Discriminator '%s' must be of enumeration " "Discriminator '%s' must be of enumeration "
"type" % discriminator) "type" % discriminator)
# Check every branch # Check every branch; don't allow an empty union
if len(members) == 0:
raise QAPIExprError(expr_info,
"Union '%s' cannot have empty 'data'" % name)
for (key, value) in members.items(): for (key, value) in members.items():
check_name(expr_info, "Member of union '%s'" % name, key) check_name(expr_info, "Member of union '%s'" % name, key)
@ -613,7 +616,11 @@ def check_alternate(expr, expr_info):
members = expr['data'] members = expr['data']
types_seen = {} types_seen = {}
# Check every branch # Check every branch; require at least two branches
if len(members) < 2:
raise QAPIExprError(expr_info,
"Alternate '%s' should have at least two branches "
"in 'data'" % name)
for (key, value) in members.items(): for (key, value) in members.items():
check_name(expr_info, "Member of alternate '%s'" % name, key) check_name(expr_info, "Member of alternate '%s'" % name, key)
@ -622,7 +629,10 @@ def check_alternate(expr, expr_info):
value, value,
allow_metas=['built-in', 'union', 'struct', 'enum']) allow_metas=['built-in', 'union', 'struct', 'enum'])
qtype = find_alternate_member_qtype(value) qtype = find_alternate_member_qtype(value)
assert qtype if not qtype:
raise QAPIExprError(expr_info,
"Alternate '%s' member '%s' cannot use "
"type '%s'" % (name, key, value))
if qtype in types_seen: if qtype in types_seen:
raise QAPIExprError(expr_info, raise QAPIExprError(expr_info,
"Alternate '%s' member '%s' can't " "Alternate '%s' member '%s' can't "
@ -814,7 +824,7 @@ def visit_event(self, name, info, arg_type):
class QAPISchemaType(QAPISchemaEntity): class QAPISchemaType(QAPISchemaEntity):
def c_type(self, is_param=False): def c_type(self, is_param=False, is_unboxed=False):
return c_name(self.name) + pointer_suffix return c_name(self.name) + pointer_suffix
def c_null(self): def c_null(self):
@ -847,7 +857,7 @@ def __init__(self, name, json_type, c_type, c_null):
def c_name(self): def c_name(self):
return self.name return self.name
def c_type(self, is_param=False): def c_type(self, is_param=False, is_unboxed=False):
if is_param and self.name == 'str': if is_param and self.name == 'str':
return 'const ' + self._c_type_name return 'const ' + self._c_type_name
return self._c_type_name return self._c_type_name
@ -881,7 +891,7 @@ def is_implicit(self):
# See QAPISchema._make_implicit_enum_type() # See QAPISchema._make_implicit_enum_type()
return self.name.endswith('Kind') return self.name.endswith('Kind')
def c_type(self, is_param=False): def c_type(self, is_param=False, is_unboxed=False):
return c_name(self.name) return c_name(self.name)
def member_names(self): def member_names(self):
@ -977,9 +987,11 @@ def c_name(self):
assert not self.is_implicit() assert not self.is_implicit()
return QAPISchemaType.c_name(self) return QAPISchemaType.c_name(self)
def c_type(self, is_param=False): def c_type(self, is_param=False, is_unboxed=False):
assert not self.is_implicit() assert not self.is_implicit()
return QAPISchemaType.c_type(self) if is_unboxed:
return c_name(self.name)
return c_name(self.name) + pointer_suffix
def json_type(self): def json_type(self):
return 'object' return 'object'
@ -1059,6 +1071,7 @@ def __init__(self, tag_name, tag_member, variants):
assert bool(tag_member) != bool(tag_name) assert bool(tag_member) != bool(tag_name)
assert (isinstance(tag_name, str) or assert (isinstance(tag_name, str) or
isinstance(tag_member, QAPISchemaObjectTypeMember)) isinstance(tag_member, QAPISchemaObjectTypeMember))
assert len(variants) > 0
for v in variants: for v in variants:
assert isinstance(v, QAPISchemaObjectTypeVariant) assert isinstance(v, QAPISchemaObjectTypeVariant)
self.tag_name = tag_name self.tag_name = tag_name

View File

@ -242,6 +242,7 @@ check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
check-qtest-generic-y += tests/qom-test$(EXESUF) check-qtest-generic-y += tests/qom-test$(EXESUF)
qapi-schema += alternate-any.json
qapi-schema += alternate-array.json qapi-schema += alternate-array.json
qapi-schema += alternate-base.json qapi-schema += alternate-base.json
qapi-schema += alternate-clash.json qapi-schema += alternate-clash.json

View File

@ -0,0 +1 @@
tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
# we do not allow the 'any' type as an alternate branch
{ 'alternate': 'Alt',
'data': { 'one': 'any',
'two': 'int' } }

View File

View File

@ -0,0 +1 @@
tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data'

View File

@ -1 +1 @@
0 1

View File

@ -1,2 +1,2 @@
# FIXME - alternates should list at least two types to be useful # alternates must list at least two types to be useful
{ 'alternate': 'Alt', 'data': { 'i': 'int' } } { 'alternate': 'Alt', 'data': { 'i': 'int' } }

View File

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

View File

@ -0,0 +1 @@
tests/qapi-schema/flat-union-empty.json:4: Union 'Union' cannot have empty 'data'

View File

@ -1 +1 @@
0 1

View File

@ -1,4 +1,4 @@
# FIXME - flat unions should not be empty # flat unions cannot be empty
{ 'enum': 'Empty', 'data': [ ] } { 'enum': 'Empty', 'data': [ ] }
{ 'struct': 'Base', 'data': { 'type': 'Empty' } } { 'struct': 'Base', 'data': { 'type': 'Empty' } }
{ 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } } { 'union': 'Union', 'base': 'Base', 'discriminator': 'type', 'data': { } }

View File

@ -1,9 +0,0 @@
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

View File

@ -73,17 +73,22 @@
'base': 'UserDefZero', 'base': 'UserDefZero',
'data': { 'string': 'str', 'enum1': 'EnumOne' } } 'data': { 'string': 'str', 'enum1': 'EnumOne' } }
{ 'struct': 'UserDefUnionBase2',
'base': 'UserDefZero',
'data': { 'string': 'str', 'enum1': 'QEnumTwo' } }
# this variant of UserDefFlatUnion defaults to a union that uses fields with # this variant of UserDefFlatUnion defaults to a union that uses fields with
# allocated types to test corner cases in the cleanup/dealloc visitor # allocated types to test corner cases in the cleanup/dealloc visitor
{ 'union': 'UserDefFlatUnion2', { 'union': 'UserDefFlatUnion2',
'base': 'UserDefUnionBase', 'base': 'UserDefUnionBase2',
'discriminator': 'enum1', 'discriminator': 'enum1',
'data': { 'value1' : 'UserDefC', # intentional forward reference 'data': { 'value1' : 'UserDefC', # intentional forward reference
'value2' : 'UserDefB', 'value2' : 'UserDefB' } }
'value3' : 'UserDefA' } }
{ 'struct': 'WrapAlternate',
'data': { 'alt': 'UserDefAlternate' } }
{ 'alternate': 'UserDefAlternate', { 'alternate': 'UserDefAlternate',
'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } } 'data': { 'udfu': 'UserDefFlatUnion', 's': 'str', 'i': 'int' } }
{ 'struct': 'UserDefC', { 'struct': 'UserDefC',
'data': { 'string1': 'str', 'string2': 'str' } } 'data': { 'string1': 'str', 'string2': 'str' } }

View File

@ -105,7 +105,7 @@ object UserDefA
member boolean: bool optional=False member boolean: bool optional=False
member a_b: int optional=True member a_b: int optional=True
alternate UserDefAlternate alternate UserDefAlternate
case uda: UserDefA case udfu: UserDefFlatUnion
case s: str case s: str
case i: int case i: int
object UserDefB object UserDefB
@ -121,11 +121,10 @@ object UserDefFlatUnion
case value2: UserDefB case value2: UserDefB
case value3: UserDefB case value3: UserDefB
object UserDefFlatUnion2 object UserDefFlatUnion2
base UserDefUnionBase base UserDefUnionBase2
tag enum1 tag enum1
case value1: UserDefC case value1: UserDefC
case value2: UserDefB case value2: UserDefB
case value3: UserDefA
object UserDefNativeListUnion object UserDefNativeListUnion
member type: UserDefNativeListUnionKind optional=False member type: UserDefNativeListUnionKind optional=False
case integer: :obj-intList-wrapper case integer: :obj-intList-wrapper
@ -167,8 +166,14 @@ object UserDefUnionBase
base UserDefZero base UserDefZero
member string: str optional=False member string: str optional=False
member enum1: EnumOne optional=False member enum1: EnumOne optional=False
object UserDefUnionBase2
base UserDefZero
member string: str optional=False
member enum1: QEnumTwo optional=False
object UserDefZero object UserDefZero
member integer: int optional=False member integer: int optional=False
object WrapAlternate
member alt: UserDefAlternate optional=False
event __ORG.QEMU_X-EVENT __org.qemu_x-Struct event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
alternate __org.qemu_x-Alt alternate __org.qemu_x-Alt
case __org.qemu_x-branch: str case __org.qemu_x-branch: str

View File

@ -0,0 +1 @@
tests/qapi-schema/union-empty.json:2: Union 'Union' cannot have empty 'data'

View File

@ -1 +1 @@
0 1

View File

@ -1,2 +1,2 @@
# FIXME - unions should not be empty # unions cannot be empty
{ 'union': 'Union', 'data': { } } { 'union': 'Union', 'data': { } }

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* QMP Input Visitor unit-tests. * QMP Input Visitor unit-tests.
* *
* Copyright (C) 2011, 2015 Red Hat Inc. * Copyright (C) 2011-2016 Red Hat Inc.
* *
* Authors: * Authors:
* Luiz Capitulino <lcapitulino@redhat.com> * Luiz Capitulino <lcapitulino@redhat.com>
@ -295,7 +295,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1); g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1);
g_assert_cmpstr(tmp->string, ==, "str"); g_assert_cmpstr(tmp->string, ==, "str");
g_assert_cmpint(tmp->integer, ==, 41); g_assert_cmpint(tmp->integer, ==, 41);
g_assert_cmpint(tmp->u.value1->boolean, ==, true); g_assert_cmpint(tmp->u.value1.boolean, ==, true);
base = qapi_UserDefFlatUnion_base(tmp); base = qapi_UserDefFlatUnion_base(tmp);
g_assert(&base->enum1 == &tmp->enum1); g_assert(&base->enum1 == &tmp->enum1);
@ -309,6 +309,7 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,
Visitor *v; Visitor *v;
Error *err = NULL; Error *err = NULL;
UserDefAlternate *tmp; UserDefAlternate *tmp;
WrapAlternate *wrap;
v = visitor_input_test_init(data, "42"); v = visitor_input_test_init(data, "42");
visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
@ -322,10 +323,44 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,
g_assert_cmpstr(tmp->u.s, ==, "string"); g_assert_cmpstr(tmp->u.s, ==, "string");
qapi_free_UserDefAlternate(tmp); qapi_free_UserDefAlternate(tmp);
v = visitor_input_test_init(data, "{'integer':1, 'string':'str', "
"'enum1':'value1', 'boolean':true}");
visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
g_assert_cmpint(tmp->type, ==, QTYPE_QDICT);
g_assert_cmpint(tmp->u.udfu.integer, ==, 1);
g_assert_cmpstr(tmp->u.udfu.string, ==, "str");
g_assert_cmpint(tmp->u.udfu.enum1, ==, ENUM_ONE_VALUE1);
g_assert_cmpint(tmp->u.udfu.u.value1.boolean, ==, true);
g_assert_cmpint(tmp->u.udfu.u.value1.has_a_b, ==, false);
qapi_free_UserDefAlternate(tmp);
v = visitor_input_test_init(data, "false"); v = visitor_input_test_init(data, "false");
visit_type_UserDefAlternate(v, NULL, &tmp, &err); visit_type_UserDefAlternate(v, NULL, &tmp, &err);
error_free_or_abort(&err); error_free_or_abort(&err);
qapi_free_UserDefAlternate(tmp); qapi_free_UserDefAlternate(tmp);
v = visitor_input_test_init(data, "{ 'alt': 42 }");
visit_type_WrapAlternate(v, NULL, &wrap, &error_abort);
g_assert_cmpint(wrap->alt->type, ==, QTYPE_QINT);
g_assert_cmpint(wrap->alt->u.i, ==, 42);
qapi_free_WrapAlternate(wrap);
v = visitor_input_test_init(data, "{ 'alt': 'string' }");
visit_type_WrapAlternate(v, NULL, &wrap, &error_abort);
g_assert_cmpint(wrap->alt->type, ==, QTYPE_QSTRING);
g_assert_cmpstr(wrap->alt->u.s, ==, "string");
qapi_free_WrapAlternate(wrap);
v = visitor_input_test_init(data, "{ 'alt': {'integer':1, 'string':'str', "
"'enum1':'value1', 'boolean':true} }");
visit_type_WrapAlternate(v, NULL, &wrap, &error_abort);
g_assert_cmpint(wrap->alt->type, ==, QTYPE_QDICT);
g_assert_cmpint(wrap->alt->u.udfu.integer, ==, 1);
g_assert_cmpstr(wrap->alt->u.udfu.string, ==, "str");
g_assert_cmpint(wrap->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE1);
g_assert_cmpint(wrap->alt->u.udfu.u.value1.boolean, ==, true);
g_assert_cmpint(wrap->alt->u.udfu.u.value1.has_a_b, ==, false);
qapi_free_WrapAlternate(wrap);
} }
static void test_visitor_in_alternate_number(TestInputVisitorData *data, static void test_visitor_in_alternate_number(TestInputVisitorData *data,

View File

@ -1,7 +1,7 @@
/* /*
* QMP Output Visitor unit-tests. * QMP Output Visitor unit-tests.
* *
* Copyright (C) 2011, 2015 Red Hat Inc. * Copyright (C) 2011-2016 Red Hat Inc.
* *
* Authors: * Authors:
* Luiz Capitulino <lcapitulino@redhat.com> * Luiz Capitulino <lcapitulino@redhat.com>
@ -403,9 +403,8 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion));
tmp->enum1 = ENUM_ONE_VALUE1; tmp->enum1 = ENUM_ONE_VALUE1;
tmp->string = g_strdup("str"); tmp->string = g_strdup("str");
tmp->u.value1 = g_malloc0(sizeof(UserDefA));
tmp->integer = 41; tmp->integer = 41;
tmp->u.value1->boolean = true; tmp->u.value1.boolean = true;
visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort); visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
arg = qmp_output_get_qobject(data->qov); arg = qmp_output_get_qobject(data->qov);
@ -427,6 +426,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
{ {
QObject *arg; QObject *arg;
UserDefAlternate *tmp; UserDefAlternate *tmp;
QDict *qdict;
tmp = g_new0(UserDefAlternate, 1); tmp = g_new0(UserDefAlternate, 1);
tmp->type = QTYPE_QINT; tmp->type = QTYPE_QINT;
@ -453,6 +453,27 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
qapi_free_UserDefAlternate(tmp); qapi_free_UserDefAlternate(tmp);
qobject_decref(arg); qobject_decref(arg);
tmp = g_new0(UserDefAlternate, 1);
tmp->type = QTYPE_QDICT;
tmp->u.udfu.integer = 1;
tmp->u.udfu.string = g_strdup("str");
tmp->u.udfu.enum1 = ENUM_ONE_VALUE1;
tmp->u.udfu.u.value1.boolean = true;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
arg = qmp_output_get_qobject(data->qov);
g_assert_cmpint(qobject_type(arg), ==, QTYPE_QDICT);
qdict = qobject_to_qdict(arg);
g_assert_cmpint(qdict_size(qdict), ==, 4);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1);
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str");
g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1");
g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
qapi_free_UserDefAlternate(tmp);
qobject_decref(arg);
} }
static void test_visitor_out_empty(TestOutputVisitorData *data, static void test_visitor_out_empty(TestOutputVisitorData *data,