From 6e2bb3ec70e2a23ba74328031c9b82f34c3d4eb9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:43 +0200 Subject: [PATCH 01/20] qapi: Update qapi-code-gen.txt example to match current code Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- docs/qapi-code-gen.txt | 142 +++++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 54 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 26312d84e8..b915c4d54a 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -258,11 +258,23 @@ Example: mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \ --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c - /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ +[Uninteresting stuff omitted...] + + void qapi_free_UserDefOneList(UserDefOneList * obj) + { + QapiDeallocVisitor *md; + Visitor *v; + + if (!obj) { + return; + } + + md = qapi_dealloc_visitor_new(); + v = qapi_dealloc_get_visitor(md); + visit_type_UserDefOneList(v, &obj, NULL, NULL); + qapi_dealloc_visitor_cleanup(md); + } - #include "qapi/qapi-dealloc-visitor.h" - #include "example-qapi-types.h" - #include "example-qapi-visit.h" void qapi_free_UserDefOne(UserDefOne * obj) { @@ -280,31 +292,37 @@ Example: } mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h - /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ - #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES - #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES +[Uninteresting stuff omitted...] - #include "qapi/qapi-types-core.h" + #ifndef EXAMPLE_QAPI_TYPES_H + #define EXAMPLE_QAPI_TYPES_H + +[Builtin types omitted...] typedef struct UserDefOne UserDefOne; typedef struct UserDefOneList { - UserDefOne *value; + union { + UserDefOne *value; + uint64_t padding; + }; struct UserDefOneList *next; } UserDefOneList; +[Functions on builtin types omitted...] + struct UserDefOne { int64_t integer; char * string; }; + void qapi_free_UserDefOneList(UserDefOneList * obj); void qapi_free_UserDefOne(UserDefOne * obj); #endif - === scripts/qapi-visit.py === Used to generate the visitor functions used to walk through and convert @@ -328,39 +346,63 @@ Example: mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \ --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c - /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ +[Uninteresting stuff omitted...] - #include "example-qapi-visit.h" + static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne ** obj, Error **errp) + { + Error *err = NULL; + visit_type_int(m, &(*obj)->integer, "integer", &err); + visit_type_str(m, &(*obj)->string, "string", &err); + + error_propagate(errp, err); + } void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp) { - visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp); - visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp); - visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp); - visit_end_struct(m, errp); + if (!error_is_set(errp)) { + Error *err = NULL; + visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); + if (!err) { + if (*obj) { + visit_type_UserDefOne_fields(m, obj, &err); + error_propagate(errp, err); + err = NULL; + } + /* Always call end_struct if start_struct succeeded. */ + visit_end_struct(m, &err); + } + error_propagate(errp, err); + } } void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) { GenericList *i, **prev = (GenericList **)obj; + Error *err = NULL; - visit_start_list(m, name, errp); + if (!error_is_set(errp)) { + visit_start_list(m, name, &err); + if (!err) { + for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { + UserDefOneList *native_i = (UserDefOneList *)i; + visit_type_UserDefOne(m, &native_i->value, NULL, &err); + } + error_propagate(errp, err); + err = NULL; - for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) { - UserDefOneList *native_i = (UserDefOneList *)i; - visit_type_UserDefOne(m, &native_i->value, NULL, errp); + /* Always call end_list if start_list succeeded. */ + visit_end_list(m, &err); + } + error_propagate(errp, err); } - - visit_end_list(m, errp); } mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h - /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ +[Uninteresting stuff omitted...] - #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT - #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT + #ifndef EXAMPLE_QAPI_VISIT_H + #define EXAMPLE_QAPI_VISIT_H - #include "qapi/qapi-visit-core.h" - #include "example-qapi-types.h" +[Visitors for builtin types omitted...] void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp); void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp); @@ -368,9 +410,6 @@ Example: #endif mdroth@illuin:~/w/qemu2.git$ -(The actual structure of the visit_type_* functions is a bit more complex -in order to propagate errors correctly and avoid leaking memory). - === scripts/qapi-commands.py === Used to generate the marshaling/dispatch functions for the commands defined @@ -391,18 +430,8 @@ $(prefix)qmp-commands.h: Function prototypes for the QMP commands Example: mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c - /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ +[Uninteresting stuff omitted...] - #include "qemu-objects.h" - #include "qapi/qmp-core.h" - #include "qapi/qapi-visit-core.h" - #include "qapi/qmp-output-visitor.h" - #include "qapi/qmp-input-visitor.h" - #include "qapi/qapi-dealloc-visitor.h" - #include "example-qapi-types.h" - #include "example-qapi-visit.h" - - #include "example-qmp-commands.h" static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) { QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); @@ -411,15 +440,16 @@ Example: v = qmp_output_get_visitor(mo); visit_type_UserDefOne(v, &ret_in, "unused", errp); + if (!error_is_set(errp)) { + *ret_out = qmp_output_get_qobject(mo); + } + qmp_output_visitor_cleanup(mo); v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOne(v, &ret_in, "unused", errp); + visit_type_UserDefOne(v, &ret_in, "unused", NULL); qapi_dealloc_visitor_cleanup(md); - - - *ret_out = qmp_output_get_qobject(mo); } - static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp) + static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) { UserDefOne * retval = NULL; QmpInputVisitor *mi; @@ -427,38 +457,42 @@ Example: Visitor *v; UserDefOne * arg1 = NULL; - mi = qmp_input_visitor_new(QOBJECT(args)); + mi = qmp_input_visitor_new_strict(QOBJECT(args)); v = qmp_input_get_visitor(mi); visit_type_UserDefOne(v, &arg1, "arg1", errp); + qmp_input_visitor_cleanup(mi); if (error_is_set(errp)) { goto out; } retval = qmp_my_command(arg1, errp); - qmp_marshal_output_my_command(retval, ret, errp); + if (!error_is_set(errp)) { + qmp_marshal_output_my_command(retval, ret, errp); + } out: md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOne(v, &arg1, "arg1", errp); + visit_type_UserDefOne(v, &arg1, "arg1", NULL); qapi_dealloc_visitor_cleanup(md); return; } static void qmp_init_marshal(void) { - qmp_register_command("my-command", qmp_marshal_input_my_command); + qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS); } qapi_init(qmp_init_marshal); mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h - /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ +[Uninteresting stuff omitted...] - #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS - #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS + #ifndef EXAMPLE_QMP_COMMANDS_H + #define EXAMPLE_QMP_COMMANDS_H #include "example-qapi-types.h" - #include "error.h" + #include "qapi/qmp/qdict.h" + #include "qapi/error.h" UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp); From f9bee751be1292c9433a95d835474dc38a134a95 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:44 +0200 Subject: [PATCH 02/20] qapi: Normalize marshalling's visitor initialization and cleanup Input and output marshalling functions do it differently. Change them to work the same: initialize the I/O visitor, use it, clean it up, initialize the dealloc visitor, use it, clean it up. This delays dealloc visitor initialization in output marshalling functions, and input visitor cleanup in input marshalling functions. No functional change, but the latter will be convenient when I change the error handling. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- docs/qapi-code-gen.txt | 8 ++++---- scripts/qapi-commands.py | 27 ++++++++++++--------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index b915c4d54a..1a635e2572 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -434,8 +434,8 @@ Example: static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) { - QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); QmpOutputVisitor *mo = qmp_output_visitor_new(); + QapiDeallocVisitor *md; Visitor *v; v = qmp_output_get_visitor(mo); @@ -444,6 +444,7 @@ Example: *ret_out = qmp_output_get_qobject(mo); } qmp_output_visitor_cleanup(mo); + md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); visit_type_UserDefOne(v, &ret_in, "unused", NULL); qapi_dealloc_visitor_cleanup(md); @@ -452,15 +453,13 @@ Example: static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) { UserDefOne * retval = NULL; - QmpInputVisitor *mi; + QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); QapiDeallocVisitor *md; Visitor *v; UserDefOne * arg1 = NULL; - mi = qmp_input_visitor_new_strict(QOBJECT(args)); v = qmp_input_get_visitor(mi); visit_type_UserDefOne(v, &arg1, "arg1", errp); - qmp_input_visitor_cleanup(mi); if (error_is_set(errp)) { goto out; @@ -471,6 +470,7 @@ Example: } out: + qmp_input_visitor_cleanup(mi); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); visit_type_UserDefOne(v, &arg1, "arg1", NULL); diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 8d9096f65c..1399826cca 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -69,16 +69,17 @@ def gen_marshal_output_call(name, ret_type): return "" return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) -def gen_visitor_input_containers_decl(args): +def gen_visitor_input_containers_decl(args, obj): ret = "" push_indent() if len(args) > 0: ret += mcgen(''' -QmpInputVisitor *mi; +QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); QapiDeallocVisitor *md; Visitor *v; -''') +''', + obj=obj) pop_indent() return ret.rstrip() @@ -106,7 +107,7 @@ bool has_%(argname)s = false; pop_indent() return ret.rstrip() -def gen_visitor_input_block(args, obj, dealloc=False): +def gen_visitor_input_block(args, dealloc=False): ret = "" errparg = 'errp' @@ -118,15 +119,14 @@ def gen_visitor_input_block(args, obj, dealloc=False): if dealloc: errparg = 'NULL' ret += mcgen(''' +qmp_input_visitor_cleanup(mi); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); ''') else: ret += mcgen(''' -mi = qmp_input_visitor_new_strict(%(obj)s); v = qmp_input_get_visitor(mi); -''', - obj=obj) +''') for argname, argtype, optional, structured in parse_args(args): if optional: @@ -151,10 +151,6 @@ visit_end_optional(v, %(errp)s); if dealloc: ret += mcgen(''' qapi_dealloc_visitor_cleanup(md); -''') - else: - ret += mcgen(''' -qmp_input_visitor_cleanup(mi); ''') pop_indent() return ret.rstrip() @@ -166,8 +162,8 @@ def gen_marshal_output(name, args, ret_type, middle_mode): ret = mcgen(''' static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) { - QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); QmpOutputVisitor *mo = qmp_output_visitor_new(); + QapiDeallocVisitor *md; Visitor *v; v = qmp_output_get_visitor(mo); @@ -176,6 +172,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o *ret_out = qmp_output_get_qobject(mo); } qmp_output_visitor_cleanup(mo); + md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); %(visitor)s(v, &ret_in, "unused", NULL); qapi_dealloc_visitor_cleanup(md); @@ -228,9 +225,9 @@ def gen_marshal_input(name, args, ret_type, middle_mode): %(visitor_input_block)s ''', - visitor_input_containers_decl=gen_visitor_input_containers_decl(args), + visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), visitor_input_vars_decl=gen_visitor_input_vars_decl(args), - visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) + visitor_input_block=gen_visitor_input_block(args)) else: ret += mcgen(''' (void)args; @@ -250,7 +247,7 @@ out: ret += mcgen(''' %(visitor_input_block_cleanup)s ''', - visitor_input_block_cleanup=gen_visitor_input_block(args, None, + visitor_input_block_cleanup=gen_visitor_input_block(args, dealloc=True)) if middle_mode: From cbc95538eda98929d2c5c8ff0d9db9043fcf1ae6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:45 +0200 Subject: [PATCH 03/20] qapi: Remove unused Visitor callbacks start_handle(), end_handle() These have never been called or implemented by anything, and their intended use is undocumented, like all of the visitor API. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- include/qapi/visitor-impl.h | 3 --- qapi/qapi-visit-core.c | 15 --------------- 2 files changed, 18 deletions(-) diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index f3fa420245..166aaddd29 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -46,9 +46,6 @@ struct Visitor Error **errp); void (*end_optional)(Visitor *v, Error **errp); - void (*start_handle)(Visitor *v, void **obj, const char *kind, - const char *name, Error **errp); - void (*end_handle)(Visitor *v, Error **errp); void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp); void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp); void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp); diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 6451a21a28..1f7475c0f3 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -17,21 +17,6 @@ #include "qapi/visitor.h" #include "qapi/visitor-impl.h" -void visit_start_handle(Visitor *v, void **obj, const char *kind, - const char *name, Error **errp) -{ - if (!error_is_set(errp) && v->start_handle) { - v->start_handle(v, obj, kind, name, errp); - } -} - -void visit_end_handle(Visitor *v, Error **errp) -{ - if (!error_is_set(errp) && v->end_handle) { - v->end_handle(v, errp); - } -} - void visit_start_struct(Visitor *v, void **obj, const char *kind, const char *name, size_t size, Error **errp) { From e2cd0f4fb42b1fae65ad22e8efde9804446e6254 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:46 +0200 Subject: [PATCH 04/20] qapi: Replace start_optional()/end_optional() by optional() Semantics of end_optional() differ subtly from the other end_FOO() callbacks: when start_FOO() succeeds, the matching end_FOO() gets called regardless of what happens in between. end_optional() gets called only when everything in between succeeds as well. Entirely undocumented, like all of the visitor API. The only user of Visitor Callback end_optional() never did anything, and was removed in commit 9f9ab46. I'm about to clean up error handling in the generated visitor code, and end_optional() is in my way. No users mean no test cases, and making non-trivial cleanup transformations without test cases doesn't strike me as a good idea. Drop end_optional(), and rename start_optional() to optional(). We can always go back to a pair of callbacks when we have an actual need. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- include/qapi/visitor-impl.h | 5 ++--- include/qapi/visitor.h | 5 ++--- qapi/opts-visitor.c | 5 ++--- qapi/qapi-visit-core.c | 15 ++++----------- qapi/qmp-input-visitor.c | 6 +++--- qapi/string-input-visitor.c | 6 +++--- scripts/qapi-commands.py | 5 ++--- scripts/qapi-visit.py | 3 +-- 8 files changed, 19 insertions(+), 31 deletions(-) diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 166aaddd29..ecc0183196 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -42,9 +42,8 @@ struct Visitor Error **errp); /* May be NULL */ - void (*start_optional)(Visitor *v, bool *present, const char *name, - Error **errp); - void (*end_optional)(Visitor *v, Error **errp); + void (*optional)(Visitor *v, bool *present, const char *name, + Error **errp); void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp); void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp); diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 29da211b47..4a0178fa46 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -39,9 +39,8 @@ void visit_end_implicit_struct(Visitor *v, Error **errp); void visit_start_list(Visitor *v, const char *name, Error **errp); GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp); void visit_end_list(Visitor *v, Error **errp); -void visit_start_optional(Visitor *v, bool *present, const char *name, - Error **errp); -void visit_end_optional(Visitor *v, Error **errp); +void visit_optional(Visitor *v, bool *present, const char *name, + Error **errp); void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, const char *name, Error **errp); void visit_type_enum(Visitor *v, int *obj, const char *strings[], diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 87c1c789c9..16382e7a65 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -484,8 +484,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) static void -opts_start_optional(Visitor *v, bool *present, const char *name, - Error **errp) +opts_optional(Visitor *v, bool *present, const char *name, Error **errp) { OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); @@ -528,7 +527,7 @@ opts_visitor_new(const QemuOpts *opts) /* type_number() is not filled in, but this is not the first visitor to * skip some mandatory methods... */ - ov->visitor.start_optional = &opts_start_optional; + ov->visitor.optional = &opts_optional; ov->opts_root = opts; diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 1f7475c0f3..ffd76372d1 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -69,18 +69,11 @@ void visit_end_list(Visitor *v, Error **errp) v->end_list(v, errp); } -void visit_start_optional(Visitor *v, bool *present, const char *name, - Error **errp) +void visit_optional(Visitor *v, bool *present, const char *name, + Error **errp) { - if (!error_is_set(errp) && v->start_optional) { - v->start_optional(v, present, name, errp); - } -} - -void visit_end_optional(Visitor *v, Error **errp) -{ - if (!error_is_set(errp) && v->end_optional) { - v->end_optional(v, errp); + if (!error_is_set(errp) && v->optional) { + v->optional(v, present, name, errp); } } diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c index a2bed1ef10..d8612062f1 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qmp-input-visitor.c @@ -286,8 +286,8 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name, } } -static void qmp_input_start_optional(Visitor *v, bool *present, - const char *name, Error **errp) +static void qmp_input_optional(Visitor *v, bool *present, const char *name, + Error **errp) { QmpInputVisitor *qiv = to_qiv(v); QObject *qobj = qmp_input_get_object(qiv, name, true); @@ -329,7 +329,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) v->visitor.type_bool = qmp_input_type_bool; v->visitor.type_str = qmp_input_type_str; v->visitor.type_number = qmp_input_type_number; - v->visitor.start_optional = qmp_input_start_optional; + v->visitor.optional = qmp_input_optional; v->visitor.get_next_type = qmp_input_get_next_type; qmp_input_push(v, obj, NULL); diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index 793548ae3a..5780944792 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -120,8 +120,8 @@ static void parse_type_number(Visitor *v, double *obj, const char *name, *obj = val; } -static void parse_start_optional(Visitor *v, bool *present, - const char *name, Error **errp) +static void parse_optional(Visitor *v, bool *present, const char *name, + Error **errp) { StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); @@ -155,7 +155,7 @@ StringInputVisitor *string_input_visitor_new(const char *str) v->visitor.type_bool = parse_type_bool; v->visitor.type_str = parse_type_str; v->visitor.type_number = parse_type_number; - v->visitor.start_optional = parse_start_optional; + v->visitor.optional = parse_optional; v->string = str; return v; diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 1399826cca..341dba27a8 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -131,7 +131,7 @@ v = qmp_input_get_visitor(mi); for argname, argtype, optional, structured in parse_args(args): if optional: ret += mcgen(''' -visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); +visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); if (has_%(c_name)s) { ''', c_name=c_var(argname), name=argname, errp=errparg) @@ -145,8 +145,7 @@ if (has_%(c_name)s) { pop_indent() ret += mcgen(''' } -visit_end_optional(v, %(errp)s); -''', errp=errparg) +''') if dealloc: ret += mcgen(''' diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index c6579beed5..9f22eee873 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -61,7 +61,7 @@ if (!err) { for argname, argentry, optional, structured in parse_args(members): if optional: ret += mcgen(''' -visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); +visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); if ((*obj)->%(prefix)shas_%(c_name)s) { ''', c_prefix=c_var(field_prefix), prefix=field_prefix, @@ -82,7 +82,6 @@ visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); pop_indent() ret += mcgen(''' } -visit_end_optional(m, &err); ''') pop_indent() From 468866b816f13c8141b692ac0280706c16b30185 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:47 +0200 Subject: [PATCH 05/20] qapi-visit.py: Clean up confusing push_indent() / pop_indent() use Changing implicit indentation in the middle of generating a block makes following the code being generated unnecessarily hard. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- scripts/qapi-visit.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 9f22eee873..28176ba734 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -128,12 +128,14 @@ if (!err) { ''', name=full_name) + ret += mcgen(''' + /* Always call end_struct if start_struct succeeded. */ + visit_end_struct(m, &err); +} +error_propagate(errp, err); +''') pop_indent() ret += mcgen(''' - /* Always call end_struct if start_struct succeeded. */ - visit_end_struct(m, &err); - } - error_propagate(errp, err); } ''') return ret @@ -289,19 +291,15 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** ''', name=name) - - push_indent() push_indent() push_indent() if base: ret += mcgen(''' - visit_type_%(name)s_fields(m, obj, &err); + visit_type_%(name)s_fields(m, obj, &err); ''', name=name) - pop_indent() - if not discriminator: disc_key = "type" else: @@ -343,19 +341,17 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** } error_propagate(errp, err); err = NULL; - } ''') pop_indent() - ret += mcgen(''' - /* Always call end_struct if start_struct succeeded. */ - visit_end_struct(m, &err); - } - error_propagate(errp, err); -} -''') + pop_indent() - pop_indent(); ret += mcgen(''' + } + /* Always call end_struct if start_struct succeeded. */ + visit_end_struct(m, &err); + } + error_propagate(errp, err); + } } ''') From 4fa953f20d418256504d24ea16f1b39791d12e5b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:48 +0200 Subject: [PATCH 06/20] qapi: Clean up shadowing of parameters and locals in inner scopes By un-inlining the visit of nested complex types. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- scripts/qapi-visit.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 28176ba734..57be9bf00c 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -35,6 +35,19 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = nested_field_prefix = "%s%s." % (field_prefix, argname) ret += generate_visit_struct_fields(name, nested_field_prefix, nested_fn_prefix, argentry) + ret += mcgen(''' + +static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp) +{ + Error *err = NULL; +''', + name=name, full_name=full_name, c_name=c_var(argname)) + push_indent() + ret += generate_visit_struct_body(full_name, argname, argentry) + pop_indent() + ret += mcgen(''' +} +''') ret += mcgen(''' @@ -69,7 +82,10 @@ if ((*obj)->%(prefix)shas_%(c_name)s) { push_indent() if structured: - ret += generate_visit_struct_body(full_name, argname, argentry) + ret += mcgen(''' +visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err); +''', + full_name=full_name, c_name=c_var(argname)) else: ret += mcgen(''' visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); @@ -106,8 +122,6 @@ if (!error_is_set(errp)) { if len(field_prefix): ret += mcgen(''' -Error **errp = &err; /* from outer scope */ -Error *err = NULL; visit_start_struct(m, NULL, "", "%(name)s", 0, &err); ''', name=name) From 192cca60aec59eda9a36501f03fc9dabf4d66a16 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:49 +0200 Subject: [PATCH 07/20] qapi-visit.py: Clean up a sloppy use of field prefix generate_visit_struct_fields() generates the base type's struct member name both with and without the field prefix. Harmless, because the field prefix is always empty there: only unboxed complex members have a prefix, and those can't have a base type. Clean it up anyway. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- scripts/qapi-visit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 57be9bf00c..1e368bedbb 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -60,7 +60,7 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * if base: ret += mcgen(''' -visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err); +visit_start_implicit_struct(m, (void**) &(*obj)->%(c_prefix)s%(c_name)s, sizeof(%(type)s), &err); if (!err) { visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); error_propagate(errp, err); From be3c771796cb5ca4e7de2e04ed3cd2e3b6b76185 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:50 +0200 Subject: [PATCH 08/20] qapi: Un-inline visit of implicit struct In preparation of error handling changes. Bonus: generates less duplicated code. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- scripts/qapi-visit.py | 48 ++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 1e368bedbb..1ef753a9b9 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -17,6 +17,31 @@ import os import getopt import errno +implicit_structs = [] + +def generate_visit_implicit_struct(type): + global implicit_structs + if type in implicit_structs: + return '' + implicit_structs.append(type) + return mcgen(''' + +static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) +{ + Error *err = NULL; + + visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err); + if (!err) { + visit_type_%(c_type)s_fields(m, obj, &err); + error_propagate(errp, err); + err = NULL; + visit_end_implicit_struct(m, &err); + } + error_propagate(errp, err); +} +''', + c_type=type_name(type)) + def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None): substructs = [] ret = '' @@ -49,6 +74,9 @@ static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj } ''') + if base: + ret += generate_visit_implicit_struct(base) + ret += mcgen(''' static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error **errp) @@ -60,13 +88,7 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * if base: ret += mcgen(''' -visit_start_implicit_struct(m, (void**) &(*obj)->%(c_prefix)s%(c_name)s, sizeof(%(type)s), &err); -if (!err) { - visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); - error_propagate(errp, err); - err = NULL; - visit_end_implicit_struct(m, &err); -} +visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); ''', c_prefix=c_var(field_prefix), type=type_name(base), c_name=c_var('base')) @@ -292,6 +314,10 @@ def generate_visit_union(expr): del base_fields[discriminator] ret += generate_visit_struct_fields(name, "", "", base_fields) + if discriminator: + for key in members: + ret += generate_visit_implicit_struct(members[key]) + ret += mcgen(''' void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) @@ -330,13 +356,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** if not discriminator: fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' else: - fmt = '''visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(c_type)s), &err); - if (!err) { - visit_type_%(c_type)s_fields(m, &(*obj)->%(c_name)s, &err); - error_propagate(errp, err); - err = NULL; - visit_end_implicit_struct(m, &err); - }''' + fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' enum_full_value = generate_enum_full_value(disc_type, key) ret += mcgen(''' From f9f3a5ecde4cb636d8eb43edc0d097bd364ffe75 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:51 +0200 Subject: [PATCH 09/20] hmp: Call visit_end_struct() after visit_start_struct() succeeds When visit_start_struct() succeeds, visit_end_struct() must be called. hmp_object_add() doesn't when a member visit fails. As far as I can tell, the opts visitor copes okay with the misuse. Fix it anyway. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- hmp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hmp.c b/hmp.c index 5c4d612294..a9d0236cc3 100644 --- a/hmp.c +++ b/hmp.c @@ -1388,6 +1388,7 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict) void hmp_object_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; + Error *err_end = NULL; QemuOpts *opts; char *type = NULL; char *id = NULL; @@ -1411,24 +1412,23 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) qdict_del(pdict, "qom-type"); visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); if (err) { - goto out_clean; + goto out_end; } qdict_del(pdict, "id"); visit_type_str(opts_get_visitor(ov), &id, "id", &err); if (err) { - goto out_clean; + goto out_end; } object_add(type, id, pdict, opts_get_visitor(ov), &err); - if (err) { - goto out_clean; - } - visit_end_struct(opts_get_visitor(ov), &err); - if (err) { + +out_end: + visit_end_struct(opts_get_visitor(ov), &err_end); + if (!err && err_end) { qmp_object_del(id, NULL); } - + error_propagate(&err, err_end); out_clean: opts_visitor_cleanup(ov); From 2ddb16a95f7d4edab4022ef4707d926c0c28db8d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:52 +0200 Subject: [PATCH 10/20] hw: Don't call visit_end_struct() after visit_start_struct() fails When visit_start_struct() fails, visit_end_struct() must not be called. rtc_get_date() and balloon_stats_all() call it anyway. As far as I can tell, they're only used with the string output visitor, which doesn't care. Fix them anyway. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- hw/timer/mc146818rtc.c | 23 +++++++++++++++-------- hw/virtio/virtio-balloon.c | 25 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 8509309fa7..6c3e3b6d75 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -793,19 +793,26 @@ static const MemoryRegionOps cmos_ops = { static void rtc_get_date(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { + Error *err = NULL; RTCState *s = MC146818_RTC(obj); struct tm current_tm; rtc_update_time(s); rtc_get_time(s, ¤t_tm); - visit_start_struct(v, NULL, "struct tm", name, 0, errp); - visit_type_int32(v, ¤t_tm.tm_year, "tm_year", errp); - visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", errp); - visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", errp); - visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", errp); - visit_type_int32(v, ¤t_tm.tm_min, "tm_min", errp); - visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", errp); - visit_end_struct(v, errp); + visit_start_struct(v, NULL, "struct tm", name, 0, &err); + if (err) { + goto out; + } + visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); + visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err); + visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err); + visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err); + visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err); + visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", &err); + visit_end_struct(v, &err); + +out: + error_propagate(errp, err); } static void rtc_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 971a921777..ca99bd5f97 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -108,6 +108,7 @@ static void balloon_stats_poll_cb(void *opaque) static void balloon_stats_get_all(Object *obj, struct Visitor *v, void *opaque, const char *name, Error **errp) { + Error *err = NULL; VirtIOBalloon *s = opaque; int i; @@ -116,17 +117,29 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v, return; } - visit_start_struct(v, NULL, "guest-stats", name, 0, errp); - visit_type_int(v, &s->stats_last_update, "last-update", errp); + visit_start_struct(v, NULL, "guest-stats", name, 0, &err); + if (err) { + goto out; + } - visit_start_struct(v, NULL, NULL, "stats", 0, errp); + visit_type_int(v, &s->stats_last_update, "last-update", &err); + + visit_start_struct(v, NULL, NULL, "stats", 0, &err); + if (err) { + goto out_end; + } + for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], - errp); + &err); } - visit_end_struct(v, errp); + visit_end_struct(v, &err); - visit_end_struct(v, errp); +out_end: + visit_end_struct(v, &err); + +out: + error_propagate(errp, err); } static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, From cdaec3808e756fee3c4e17d0168ec6429eff0dbc Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:53 +0200 Subject: [PATCH 11/20] tests: Don't call visit_end_struct() after visit_start_struct() fails When visit_start_struct() fails, visit_end_struct() must not be called. Three out of four visit_type_TestStruct() call it anyway. As far as I can tell, visit_start_struct() doesn't actually fail there. Fix them anyway. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- tests/test-qmp-input-strict.c | 18 +++++++++++++----- tests/test-qmp-output-visitor.c | 18 +++++++++++++----- tests/test-visitor-serialization.c | 18 +++++++++++++----- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index 449d285e56..ec798c2acf 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -72,14 +72,22 @@ typedef struct TestStruct static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) { + Error *err = NULL; + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), - errp); + &err); + if (err) { + goto out; + } - visit_type_int(v, &(*obj)->integer, "integer", errp); - visit_type_bool(v, &(*obj)->boolean, "boolean", errp); - visit_type_str(v, &(*obj)->string, "string", errp); + visit_type_int(v, &(*obj)->integer, "integer", &err); + visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + visit_type_str(v, &(*obj)->string, "string", &err); - visit_end_struct(v, errp); + visit_end_struct(v, &err); + +out: + error_propagate(errp, err); } static void test_validate_struct(TestInputVisitorData *data, diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index 2580f3debf..dfd597cee1 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -176,14 +176,22 @@ typedef struct TestStruct static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) { + Error *err = NULL; + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), - errp); + &err); + if (err) { + goto out; + } - visit_type_int(v, &(*obj)->integer, "integer", errp); - visit_type_bool(v, &(*obj)->boolean, "boolean", errp); - visit_type_str(v, &(*obj)->string, "string", errp); + visit_type_int(v, &(*obj)->integer, "integer", &err); + visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + visit_type_str(v, &(*obj)->string, "string", &err); - visit_end_struct(v, errp); + visit_end_struct(v, &err); + +out: + error_propagate(errp, err); } static void test_visitor_out_struct(TestOutputVisitorData *data, diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 8166cf1b05..85170e5c49 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -195,13 +195,21 @@ typedef struct TestStruct static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) { - visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp); + Error *err= NULL; - visit_type_int(v, &(*obj)->integer, "integer", errp); - visit_type_bool(v, &(*obj)->boolean, "boolean", errp); - visit_type_str(v, &(*obj)->string, "string", errp); + visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err); + if (err) { + goto out; + } - visit_end_struct(v, errp); + visit_type_int(v, &(*obj)->integer, "integer", &err); + visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + visit_type_str(v, &(*obj)->string, "string", &err); + + visit_end_struct(v, &err); + +out: + error_propagate(errp, err); } static TestStruct *struct_create(void) From 297a3646c2947ee64a6d42ca264039732c6218e0 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 7 May 2014 09:53:54 +0200 Subject: [PATCH 12/20] qapi: Replace uncommon use of the error API by the common one We commonly use the error API like this: err = NULL; foo(..., &err); if (err) { goto out; } bar(..., &err); Every error source is checked separately. The second function is only called when the first one succeeds. Both functions are free to pass their argument to error_set(). Because error_set() asserts no error has been set, this effectively means they must not be called with an error set. The qapi-generated code uses the error API differently: // *errp was initialized to NULL somewhere up the call chain frob(..., errp); gnat(..., errp); Errors accumulate in *errp: first error wins, subsequent errors get dropped. To make this work, the second function does nothing when called with an error set. Requires non-null errp, or else the second function can't see the first one fail. This usage has also bled into visitor tests, and two device model object property getters rtc_get_date() and balloon_stats_get_all(). With the "accumulate" technique, you need fewer error checks in callers, and buy that with an error check in every callee. Can be nice. However, mixing the two techniques is confusing. You can't use the "accumulate" technique with functions designed for the "check separately" technique. You can use the "check separately" technique with functions designed for the "accumulate" technique, but then error_set() can't catch you setting an error more than once. Standardize on the "check separately" technique for now, because it's overwhelmingly prevalent. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- docs/qapi-code-gen.txt | 87 ++++++----- hw/timer/mc146818rtc.c | 24 ++- hw/virtio/virtio-balloon.c | 12 +- qapi/qapi-visit-core.c | 231 +++++++++++++---------------- scripts/qapi-commands.py | 57 +++++-- scripts/qapi-visit.py | 171 ++++++++++----------- tests/test-qmp-input-strict.c | 10 +- tests/test-qmp-input-visitor.c | 26 ++-- tests/test-qmp-output-visitor.c | 10 +- tests/test-visitor-serialization.c | 12 +- 10 files changed, 354 insertions(+), 286 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 1a635e2572..1948946c05 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -275,7 +275,6 @@ Example: qapi_dealloc_visitor_cleanup(md); } - void qapi_free_UserDefOne(UserDefOne * obj) { QapiDeallocVisitor *md; @@ -352,49 +351,54 @@ Example: { Error *err = NULL; visit_type_int(m, &(*obj)->integer, "integer", &err); + if (err) { + goto out; + } visit_type_str(m, &(*obj)->string, "string", &err); + if (err) { + goto out; + } + out: error_propagate(errp, err); } void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp) { - if (!error_is_set(errp)) { - Error *err = NULL; - visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); - if (!err) { - if (*obj) { - visit_type_UserDefOne_fields(m, obj, &err); - error_propagate(errp, err); - err = NULL; - } - /* Always call end_struct if start_struct succeeded. */ - visit_end_struct(m, &err); + Error *err = NULL; + + visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); + if (!err) { + if (*obj) { + visit_type_UserDefOne_fields(m, obj, errp); } - error_propagate(errp, err); + visit_end_struct(m, &err); } + error_propagate(errp, err); } void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) { - GenericList *i, **prev = (GenericList **)obj; Error *err = NULL; + GenericList *i, **prev; - if (!error_is_set(errp)) { - visit_start_list(m, name, &err); - if (!err) { - for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { - UserDefOneList *native_i = (UserDefOneList *)i; - visit_type_UserDefOne(m, &native_i->value, NULL, &err); - } - error_propagate(errp, err); - err = NULL; - - /* Always call end_list if start_list succeeded. */ - visit_end_list(m, &err); - } - error_propagate(errp, err); + visit_start_list(m, name, &err); + if (err) { + goto out; } + + for (prev = (GenericList **)obj; + !err && (i = visit_next_list(m, prev, &err)) != NULL; + prev = &i) { + UserDefOneList *native_i = (UserDefOneList *)i; + visit_type_UserDefOne(m, &native_i->value, NULL, &err); + } + + error_propagate(errp, err); + err = NULL; + visit_end_list(m, &err); + out: + error_propagate(errp, err); } mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h [Uninteresting stuff omitted...] @@ -434,15 +438,20 @@ Example: static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) { + Error *local_err = NULL; QmpOutputVisitor *mo = qmp_output_visitor_new(); QapiDeallocVisitor *md; Visitor *v; v = qmp_output_get_visitor(mo); - visit_type_UserDefOne(v, &ret_in, "unused", errp); - if (!error_is_set(errp)) { - *ret_out = qmp_output_get_qobject(mo); + visit_type_UserDefOne(v, &ret_in, "unused", &local_err); + if (local_err) { + goto out; } + *ret_out = qmp_output_get_qobject(mo); + + out: + error_propagate(errp, local_err); qmp_output_visitor_cleanup(mo); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); @@ -452,6 +461,7 @@ Example: static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) { + Error *local_err = NULL; UserDefOne * retval = NULL; QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); QapiDeallocVisitor *md; @@ -459,17 +469,20 @@ Example: UserDefOne * arg1 = NULL; v = qmp_input_get_visitor(mi); - visit_type_UserDefOne(v, &arg1, "arg1", errp); - - if (error_is_set(errp)) { + visit_type_UserDefOne(v, &arg1, "arg1", &local_err); + if (local_err) { goto out; } - retval = qmp_my_command(arg1, errp); - if (!error_is_set(errp)) { - qmp_marshal_output_my_command(retval, ret, errp); + + retval = qmp_my_command(arg1, &local_err); + if (local_err) { + goto out; } + qmp_marshal_output_my_command(retval, ret, &local_err); + out: + error_propagate(errp, local_err); qmp_input_visitor_cleanup(mi); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 6c3e3b6d75..df54546562 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -804,13 +804,33 @@ static void rtc_get_date(Object *obj, Visitor *v, void *opaque, goto out; } visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); + if (err) { + goto out_end; + } visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err); + if (err) { + goto out_end; + } visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err); + if (err) { + goto out_end; + } visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err); + if (err) { + goto out_end; + } visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err); + if (err) { + goto out_end; + } visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", &err); - visit_end_struct(v, &err); - + if (err) { + goto out_end; + } +out_end: + error_propagate(errp, err); + err = NULL; + visit_end_struct(v, errp); out: error_propagate(errp, err); } diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index ca99bd5f97..bf2b588b24 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -121,23 +121,27 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v, if (err) { goto out; } - visit_type_int(v, &s->stats_last_update, "last-update", &err); + if (err) { + goto out_end; + } visit_start_struct(v, NULL, NULL, "stats", 0, &err); if (err) { goto out_end; } - - for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { + for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) { visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], &err); } + error_propagate(errp, err); + err = NULL; visit_end_struct(v, &err); out_end: + error_propagate(errp, err); + err = NULL; visit_end_struct(v, &err); - out: error_propagate(errp, err); } diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index ffd76372d1..55f8d4068c 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -20,28 +20,24 @@ void visit_start_struct(Visitor *v, void **obj, const char *kind, const char *name, size_t size, Error **errp) { - if (!error_is_set(errp)) { - v->start_struct(v, obj, kind, name, size, errp); - } + v->start_struct(v, obj, kind, name, size, errp); } void visit_end_struct(Visitor *v, Error **errp) { - assert(!error_is_set(errp)); v->end_struct(v, errp); } void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, Error **errp) { - if (!error_is_set(errp) && v->start_implicit_struct) { + if (v->start_implicit_struct) { v->start_implicit_struct(v, obj, size, errp); } } void visit_end_implicit_struct(Visitor *v, Error **errp) { - assert(!error_is_set(errp)); if (v->end_implicit_struct) { v->end_implicit_struct(v, errp); } @@ -49,30 +45,23 @@ void visit_end_implicit_struct(Visitor *v, Error **errp) void visit_start_list(Visitor *v, const char *name, Error **errp) { - if (!error_is_set(errp)) { - v->start_list(v, name, errp); - } + v->start_list(v, name, errp); } GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) { - if (!error_is_set(errp)) { - return v->next_list(v, list, errp); - } - - return 0; + return v->next_list(v, list, errp); } void visit_end_list(Visitor *v, Error **errp) { - assert(!error_is_set(errp)); v->end_list(v, errp); } void visit_optional(Visitor *v, bool *present, const char *name, Error **errp) { - if (!error_is_set(errp) && v->optional) { + if (v->optional) { v->optional(v, present, name, errp); } } @@ -80,7 +69,7 @@ void visit_optional(Visitor *v, bool *present, const char *name, void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, const char *name, Error **errp) { - if (!error_is_set(errp) && v->get_next_type) { + if (v->get_next_type) { v->get_next_type(v, obj, qtypes, name, errp); } } @@ -88,192 +77,172 @@ void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, void visit_type_enum(Visitor *v, int *obj, const char *strings[], const char *kind, const char *name, Error **errp) { - if (!error_is_set(errp)) { - v->type_enum(v, obj, strings, kind, name, errp); - } + v->type_enum(v, obj, strings, kind, name, errp); } void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) { - if (!error_is_set(errp)) { - v->type_int(v, obj, name, errp); - } + v->type_int(v, obj, name, errp); } void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_uint8) { - v->type_uint8(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < 0 || value > UINT8_MAX) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "uint8_t"); - return; - } - *obj = value; + + if (v->type_uint8) { + v->type_uint8(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + if (value < 0 || value > UINT8_MAX) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "uint8_t"); + return; } + *obj = value; } } void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_uint16) { - v->type_uint16(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < 0 || value > UINT16_MAX) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "uint16_t"); - return; - } - *obj = value; + + if (v->type_uint16) { + v->type_uint16(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + if (value < 0 || value > UINT16_MAX) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "uint16_t"); + return; } + *obj = value; } } void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_uint32) { - v->type_uint32(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < 0 || value > UINT32_MAX) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "uint32_t"); - return; - } - *obj = value; + + if (v->type_uint32) { + v->type_uint32(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + if (value < 0 || value > UINT32_MAX) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "uint32_t"); + return; } + *obj = value; } } void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_uint64) { - v->type_uint64(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - *obj = value; - } + + if (v->type_uint64) { + v->type_uint64(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + *obj = value; } } void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_int8) { - v->type_int8(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < INT8_MIN || value > INT8_MAX) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "int8_t"); - return; - } - *obj = value; + + if (v->type_int8) { + v->type_int8(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + if (value < INT8_MIN || value > INT8_MAX) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "int8_t"); + return; } + *obj = value; } } void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_int16) { - v->type_int16(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < INT16_MIN || value > INT16_MAX) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "int16_t"); - return; - } - *obj = value; + + if (v->type_int16) { + v->type_int16(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + if (value < INT16_MIN || value > INT16_MAX) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "int16_t"); + return; } + *obj = value; } } void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_int32) { - v->type_int32(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - if (value < INT32_MIN || value > INT32_MAX) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "int32_t"); - return; - } - *obj = value; + + if (v->type_int32) { + v->type_int32(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + if (value < INT32_MIN || value > INT32_MAX) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "int32_t"); + return; } + *obj = value; } } void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp) { - if (!error_is_set(errp)) { - if (v->type_int64) { - v->type_int64(v, obj, name, errp); - } else { - v->type_int(v, obj, name, errp); - } + if (v->type_int64) { + v->type_int64(v, obj, name, errp); + } else { + v->type_int(v, obj, name, errp); } } void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) { int64_t value; - if (!error_is_set(errp)) { - if (v->type_size) { - v->type_size(v, obj, name, errp); - } else if (v->type_uint64) { - v->type_uint64(v, obj, name, errp); - } else { - value = *obj; - v->type_int(v, &value, name, errp); - *obj = value; - } + + if (v->type_size) { + v->type_size(v, obj, name, errp); + } else if (v->type_uint64) { + v->type_uint64(v, obj, name, errp); + } else { + value = *obj; + v->type_int(v, &value, name, errp); + *obj = value; } } void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) { - if (!error_is_set(errp)) { - v->type_bool(v, obj, name, errp); - } + v->type_bool(v, obj, name, errp); } void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) { - if (!error_is_set(errp)) { - v->type_str(v, obj, name, errp); - } + v->type_str(v, obj, name, errp); } void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) { - if (!error_is_set(errp)) { - v->type_number(v, obj, name, errp); - } + v->type_number(v, obj, name, errp); } void output_type_enum(Visitor *v, int *obj, const char *strings[], @@ -299,13 +268,15 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[], const char *kind, const char *name, Error **errp) { + Error *local_err = NULL; int64_t value = 0; char *enum_str; assert(strings); - visit_type_str(v, &enum_str, name, errp); - if (error_is_set(errp)) { + visit_type_str(v, &enum_str, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 341dba27a8..386f17ef44 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -2,16 +2,19 @@ # QAPI command marshaller generator # # Copyright IBM, Corp. 2011 +# Copyright (C) 2014 Red Hat, Inc. # # Authors: # Anthony Liguori # Michael Roth +# Markus Armbruster # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * +import re import sys import os import getopt @@ -37,6 +40,15 @@ def generate_command_decl(name, args, ret_type): ''', ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() +def gen_err_check(errvar): + if errvar: + return mcgen(''' +if (local_err) { + goto out; +} +''') + return '' + def gen_sync_call(name, args, ret_type, indent=0): ret = "" arglist="" @@ -49,15 +61,14 @@ def gen_sync_call(name, args, ret_type, indent=0): arglist += "%s, " % (c_var(argname)) push_indent(indent) ret = mcgen(''' -%(retval)sqmp_%(name)s(%(args)serrp); +%(retval)sqmp_%(name)s(%(args)s&local_err); ''', name=c_fun(name), args=arglist, retval=retval).rstrip() if ret_type: + ret += "\n" + gen_err_check('local_err') ret += "\n" + mcgen('''' -if (!error_is_set(errp)) { - %(marshal_output_call)s -} +%(marshal_output_call)s ''', marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() pop_indent(indent) @@ -67,7 +78,7 @@ if (!error_is_set(errp)) { def gen_marshal_output_call(name, ret_type): if not ret_type: return "" - return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) + return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_fun(name) def gen_visitor_input_containers_decl(args, obj): ret = "" @@ -109,7 +120,8 @@ bool has_%(argname)s = false; def gen_visitor_input_block(args, dealloc=False): ret = "" - errparg = 'errp' + errparg = '&local_err' + errarg = 'local_err' if len(args) == 0: return ret @@ -118,6 +130,7 @@ def gen_visitor_input_block(args, dealloc=False): if dealloc: errparg = 'NULL' + errarg = None; ret += mcgen(''' qmp_input_visitor_cleanup(mi); md = qapi_dealloc_visitor_new(); @@ -132,15 +145,20 @@ v = qmp_input_get_visitor(mi); if optional: ret += mcgen(''' visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); -if (has_%(c_name)s) { ''', c_name=c_var(argname), name=argname, errp=errparg) + ret += gen_err_check(errarg) + ret += mcgen(''' +if (has_%(c_name)s) { +''', + c_name=c_var(argname)) push_indent() ret += mcgen(''' %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); ''', c_name=c_var(argname), name=argname, argtype=argtype, visitor=type_visitor(argtype), errp=errparg) + ret += gen_err_check(errarg) if optional: pop_indent() ret += mcgen(''' @@ -161,15 +179,20 @@ def gen_marshal_output(name, args, ret_type, middle_mode): ret = mcgen(''' static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) { + Error *local_err = NULL; QmpOutputVisitor *mo = qmp_output_visitor_new(); QapiDeallocVisitor *md; Visitor *v; v = qmp_output_get_visitor(mo); - %(visitor)s(v, &ret_in, "unused", errp); - if (!error_is_set(errp)) { - *ret_out = qmp_output_get_qobject(mo); + %(visitor)s(v, &ret_in, "unused", &local_err); + if (local_err) { + goto out; } + *ret_out = qmp_output_get_qobject(mo); + +out: + error_propagate(errp, local_err); qmp_output_visitor_cleanup(mo); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); @@ -196,13 +219,12 @@ def gen_marshal_input(name, args, ret_type, middle_mode): ret = mcgen(''' %(header)s { + Error *local_err = NULL; ''', header=hdr) if middle_mode: ret += mcgen(''' - Error *local_err = NULL; - Error **errp = &local_err; QDict *args = (QDict *)qdict; ''') @@ -229,19 +251,22 @@ def gen_marshal_input(name, args, ret_type, middle_mode): visitor_input_block=gen_visitor_input_block(args)) else: ret += mcgen(''' + (void)args; ''') ret += mcgen(''' - if (error_is_set(errp)) { - goto out; - } %(sync_call)s ''', sync_call=gen_sync_call(name, args, ret_type, indent=4)) - ret += mcgen(''' + if re.search('^ *goto out\\;', ret, re.MULTILINE): + ret += mcgen(''' out: +''') + if not middle_mode: + ret += mcgen(''' + error_propagate(errp, local_err); ''') ret += mcgen(''' %(visitor_input_block_cleanup)s diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 1ef753a9b9..06a79f1631 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -2,16 +2,19 @@ # QAPI visitor generator # # Copyright IBM, Corp. 2011 +# Copyright (C) 2014 Red Hat, Inc. # # Authors: # Anthony Liguori # Michael Roth +# Markus Armbruster # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * +import re import sys import os import getopt @@ -32,9 +35,7 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error * visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err); if (!err) { - visit_type_%(c_type)s_fields(m, obj, &err); - error_propagate(errp, err); - err = NULL; + visit_type_%(c_type)s_fields(m, obj, errp); visit_end_implicit_struct(m, &err); } error_propagate(errp, err); @@ -64,12 +65,9 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp) { - Error *err = NULL; ''', name=name, full_name=full_name, c_name=c_var(argname)) - push_indent() ret += generate_visit_struct_body(full_name, argname, argentry) - pop_indent() ret += mcgen(''' } ''') @@ -89,6 +87,9 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * if base: ret += mcgen(''' visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); +if (err) { + goto out; +} ''', c_prefix=c_var(field_prefix), type=type_name(base), c_name=c_var('base')) @@ -97,7 +98,7 @@ visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); if optional: ret += mcgen(''' visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); -if ((*obj)->%(prefix)shas_%(c_name)s) { +if (!err && (*obj)->%(prefix)shas_%(c_name)s) { ''', c_prefix=c_var(field_prefix), prefix=field_prefix, c_name=c_var(argname), name=argname) @@ -120,11 +121,20 @@ visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); pop_indent() ret += mcgen(''' } +''') + ret += mcgen(''' +if (err) { + goto out; +} ''') pop_indent() - ret += mcgen(''' + if re.search('^ *goto out\\;', ret, re.MULTILINE): + ret += mcgen(''' +out: +''') + ret += mcgen(''' error_propagate(errp, err); } ''') @@ -133,9 +143,9 @@ visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); def generate_visit_struct_body(field_prefix, name, members): ret = mcgen(''' -if (!error_is_set(errp)) { + Error *err = NULL; + ''') - push_indent() if not field_prefix: full_name = name @@ -144,36 +154,26 @@ if (!error_is_set(errp)) { if len(field_prefix): ret += mcgen(''' -visit_start_struct(m, NULL, "", "%(name)s", 0, &err); + visit_start_struct(m, NULL, "", "%(name)s", 0, &err); ''', name=name) else: ret += mcgen(''' -Error *err = NULL; -visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); ''', name=name) ret += mcgen(''' -if (!err) { - if (*obj) { - visit_type_%(name)s_fields(m, obj, &err); - error_propagate(errp, err); - err = NULL; + if (!err) { + if (*obj) { + visit_type_%(name)s_fields(m, obj, errp); + } + visit_end_struct(m, &err); } + error_propagate(errp, err); ''', name=full_name) - ret += mcgen(''' - /* Always call end_struct if start_struct succeeded. */ - visit_end_struct(m, &err); -} -error_propagate(errp, err); -''') - pop_indent() - ret += mcgen(''' -} -''') return ret def generate_visit_struct(expr): @@ -191,9 +191,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** ''', name=name) - push_indent() ret += generate_visit_struct_body("", name, members) - pop_indent() ret += mcgen(''' } @@ -205,24 +203,26 @@ def generate_visit_list(name, members): void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) { - GenericList *i, **prev = (GenericList **)obj; Error *err = NULL; + GenericList *i, **prev; - if (!error_is_set(errp)) { - visit_start_list(m, name, &err); - if (!err) { - for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { - %(name)sList *native_i = (%(name)sList *)i; - visit_type_%(name)s(m, &native_i->value, NULL, &err); - } - error_propagate(errp, err); - err = NULL; - - /* Always call end_list if start_list succeeded. */ - visit_end_list(m, &err); - } - error_propagate(errp, err); + visit_start_list(m, name, &err); + if (err) { + goto out; } + + for (prev = (GenericList **)obj; + !err && (i = visit_next_list(m, prev, &err)) != NULL; + prev = &i) { + %(name)sList *native_i = (%(name)sList *)i; + visit_type_%(name)s(m, &native_i->value, NULL, &err); + } + + error_propagate(errp, err); + err = NULL; + visit_end_list(m, &err); +out: + error_propagate(errp, err); } ''', name=name) @@ -244,10 +244,15 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** { Error *err = NULL; - if (!error_is_set(errp)) { - visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); - visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); - switch ((*obj)->kind) { + visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); + if (err) { + goto out; + } + visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); + if (err) { + goto out_end; + } + switch ((*obj)->kind) { ''', name=name) @@ -262,22 +267,24 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** enum_full_value = generate_enum_full_value(disc_type, key) ret += mcgen(''' - case %(enum_full_value)s: - visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); - break; + case %(enum_full_value)s: + visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); + break; ''', enum_full_value = enum_full_value, c_type = type_name(members[key]), c_name = c_fun(key)) ret += mcgen(''' - default: - abort(); - } - error_propagate(errp, err); - err = NULL; - visit_end_implicit_struct(m, &err); + default: + abort(); } +out_end: + error_propagate(errp, err); + err = NULL; + visit_end_implicit_struct(m, &err); +out: + error_propagate(errp, err); } ''') @@ -324,19 +331,20 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** { Error *err = NULL; - if (!error_is_set(errp)) { - visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); - if (!err) { - if (*obj) { + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + if (err) { + goto out; + } + if (*obj) { ''', name=name) - push_indent() - push_indent() - if base: ret += mcgen(''' visit_type_%(name)s_fields(m, obj, &err); + if (err) { + goto out_obj; + } ''', name=name) @@ -346,8 +354,10 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** disc_key = discriminator ret += mcgen(''' visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); - if (!err) { - switch ((*obj)->kind) { + if (err) { + goto out_obj; + } + switch ((*obj)->kind) { ''', disc_type = disc_type, disc_key = disc_key) @@ -360,32 +370,25 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** enum_full_value = generate_enum_full_value(disc_type, key) ret += mcgen(''' - case %(enum_full_value)s: - ''' + fmt + ''' - break; + case %(enum_full_value)s: + ''' + fmt + ''' + break; ''', enum_full_value = enum_full_value, c_type=type_name(members[key]), c_name=c_fun(key)) ret += mcgen(''' - default: - abort(); - } + default: + abort(); } +out_obj: error_propagate(errp, err); err = NULL; -''') - pop_indent() - pop_indent() - - ret += mcgen(''' - } - /* Always call end_struct if start_struct succeeded. */ - visit_end_struct(m, &err); - } - error_propagate(errp, err); } + visit_end_struct(m, &err); +out: + error_propagate(errp, err); } ''') @@ -505,7 +508,7 @@ fdecl.write(mcgen(''' /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ /* - * schema-defined QAPI visitor function + * schema-defined QAPI visitor functions * * Copyright IBM, Corp. 2011 * diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index ec798c2acf..0f770034b1 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -81,11 +81,19 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, } visit_type_int(v, &(*obj)->integer, "integer", &err); + if (err) { + goto out_end; + } visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + if (err) { + goto out_end; + } visit_type_str(v, &(*obj)->string, "string", &err); +out_end: + error_propagate(errp, err); + err = NULL; visit_end_struct(v, &err); - out: error_propagate(errp, err); } diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index a58a3e6fdb..1c8e87295c 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -199,16 +199,24 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), &err); - if (!err) { - visit_type_int(v, &(*obj)->integer, "integer", &err); - visit_type_bool(v, &(*obj)->boolean, "boolean", &err); - visit_type_str(v, &(*obj)->string, "string", &err); - - /* Always call end_struct if start_struct succeeded. */ - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); + if (err) { + goto out; } + visit_type_int(v, &(*obj)->integer, "integer", &err); + if (err) { + goto out_end; + } + visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + if (err) { + goto out_end; + } + visit_type_str(v, &(*obj)->string, "string", &err); + +out_end: + error_propagate(errp, err); + err = NULL; + visit_end_struct(v, &err); +out: error_propagate(errp, err); } diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index dfd597cee1..9c154581d7 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -185,11 +185,19 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, } visit_type_int(v, &(*obj)->integer, "integer", &err); + if (err) { + goto out_end; + } visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + if (err) { + goto out_end; + } visit_type_str(v, &(*obj)->string, "string", &err); +out_end: + error_propagate(errp, err); + err = NULL; visit_end_struct(v, &err); - out: error_propagate(errp, err); } diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 85170e5c49..74d6481992 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -195,7 +195,7 @@ typedef struct TestStruct static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) { - Error *err= NULL; + Error *err = NULL; visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err); if (err) { @@ -203,11 +203,19 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, } visit_type_int(v, &(*obj)->integer, "integer", &err); + if (err) { + goto out_end; + } visit_type_bool(v, &(*obj)->boolean, "boolean", &err); + if (err) { + goto out_end; + } visit_type_str(v, &(*obj)->string, "string", &err); +out_end: + error_propagate(errp, err); + err = NULL; visit_end_struct(v, &err); - out: error_propagate(errp, err); } From 87a560c455dfc53c151f7f5dd4a305f6d44e00dd Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 14 May 2014 17:27:23 +0200 Subject: [PATCH 13/20] qapi: Show qapi-commands.py invocation in qapi-code-gen.txt While there, pare down the shell prompts. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- docs/qapi-code-gen.txt | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 1948946c05..867d124fe2 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -230,14 +230,13 @@ node structure that can be used to chain together a list of such types in case we want to accept/return a list of this type with a command), and a command which takes that type as a parameter and returns the same type: - mdroth@illuin:~/w/qemu2.git$ cat example-schema.json + $ cat example-schema.json { 'type': 'UserDefOne', 'data': { 'integer': 'int', 'string': 'str' } } { 'command': 'my-command', 'data': {'arg1': 'UserDefOne'}, 'returns': 'UserDefOne' } - mdroth@illuin:~/w/qemu2.git$ === scripts/qapi-types.py === @@ -255,9 +254,9 @@ created code. Example: - mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \ - --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c + $ python scripts/qapi-types.py --output-dir="qapi-generated" \ + --prefix="example-" --input-file=example-schema.json + $ cat qapi-generated/example-qapi-types.c [Uninteresting stuff omitted...] void qapi_free_UserDefOneList(UserDefOneList * obj) @@ -290,7 +289,7 @@ Example: qapi_dealloc_visitor_cleanup(md); } - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h + $ cat qapi-generated/example-qapi-types.h [Uninteresting stuff omitted...] #ifndef EXAMPLE_QAPI_TYPES_H @@ -342,9 +341,9 @@ $(prefix)qapi-visit.h: declarations for previously mentioned visitor Example: - mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \ - --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c + $ python scripts/qapi-visit.py --output-dir="qapi-generated" + --prefix="example-" --input-file=example-schema.json + $ cat qapi-generated/example-qapi-visit.c [Uninteresting stuff omitted...] static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne ** obj, Error **errp) @@ -400,7 +399,9 @@ Example: out: error_propagate(errp, err); } - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h + $ python scripts/qapi-commands.py --output-dir="qapi-generated" \ + --prefix="example-" --input-file=example-schema.json + $ cat qapi-generated/example-qapi-visit.h [Uninteresting stuff omitted...] #ifndef EXAMPLE_QAPI_VISIT_H @@ -412,7 +413,6 @@ Example: void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp); #endif - mdroth@illuin:~/w/qemu2.git$ === scripts/qapi-commands.py === @@ -433,7 +433,7 @@ $(prefix)qmp-commands.h: Function prototypes for the QMP commands Example: - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c + $ cat qapi-generated/example-qmp-marshal.c [Uninteresting stuff omitted...] static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) @@ -497,7 +497,7 @@ Example: } qapi_init(qmp_init_marshal); - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h + $ cat qapi-generated/example-qmp-commands.h [Uninteresting stuff omitted...] #ifndef EXAMPLE_QMP_COMMANDS_H @@ -510,4 +510,3 @@ Example: UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp); #endif - mdroth@illuin:~/w/qemu2.git$ From 29136cd8a4883f2ce97387f3a01c156ac1c43869 Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Wed, 7 May 2014 23:41:27 +0100 Subject: [PATCH 14/20] monitor: Convert sendkey to use command_completion. Signed-off-by: Hani Benhabiles Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 1 + hmp.h | 1 + monitor.c | 32 +++++++++++++++++++++++--------- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 8971f1b153..b4b23c8b04 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -556,6 +556,7 @@ ETEXI .params = "keys [hold_ms]", .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", .mhandler.cmd = hmp_send_key, + .command_completion = sendkey_completion, }, STEXI diff --git a/hmp.h b/hmp.h index 20ef454b4a..12e21e73dd 100644 --- a/hmp.h +++ b/hmp.h @@ -97,5 +97,6 @@ void object_add_completion(ReadLineState *rs, int nb_args, const char *str); void object_del_completion(ReadLineState *rs, int nb_args, const char *str); void device_add_completion(ReadLineState *rs, int nb_args, const char *str); void device_del_completion(ReadLineState *rs, int nb_args, const char *str); +void sendkey_completion(ReadLineState *rs, int nb_args, const char *str); #endif diff --git a/monitor.c b/monitor.c index 9af6b0ad66..cb9b2c25bb 100644 --- a/monitor.c +++ b/monitor.c @@ -4376,6 +4376,28 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str) qapi_free_ObjectPropertyInfoList(start); } +void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int i; + char *sep; + size_t len; + + if (nb_args != 2) { + return; + } + sep = strrchr(str, '-'); + if (sep) { + str = sep + 1; + } + len = strlen(str); + readline_set_completion_index(rs, len); + for (i = 0; i < Q_KEY_CODE_MAX; i++) { + if (!strncmp(str, QKeyCode_lookup[i], len)) { + readline_add_completion(rs, QKeyCode_lookup[i]); + } + } +} + static void monitor_find_completion_by_table(Monitor *mon, const mon_cmd_t *cmd_table, char **args, @@ -4444,15 +4466,7 @@ static void monitor_find_completion_by_table(Monitor *mon, break; case 's': case 'S': - if (!strcmp(cmd->name, "sendkey")) { - char *sep = strrchr(str, '-'); - if (sep) - str = sep + 1; - readline_set_completion_index(mon->rs, strlen(str)); - for (i = 0; i < Q_KEY_CODE_MAX; i++) { - cmd_completion(mon, str, QKeyCode_lookup[i]); - } - } else if (!strcmp(cmd->name, "help|?")) { + if (!strcmp(cmd->name, "help|?")) { monitor_find_completion_by_table(mon, cmd_table, &args[1], nb_args - 1); } From 6297d9a279ccaf404d26a2c6bdc1a09891bcf5ae Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Wed, 7 May 2014 23:41:28 +0100 Subject: [PATCH 15/20] monitor: Add chardev-remove command completion. Signed-off-by: Hani Benhabiles Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 1 + hmp.h | 1 + monitor.c | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index b4b23c8b04..ba88e2a183 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1639,6 +1639,7 @@ ETEXI .params = "id", .help = "remove chardev", .mhandler.cmd = hmp_chardev_remove, + .command_completion = chardev_remove_completion, }, STEXI diff --git a/hmp.h b/hmp.h index 12e21e73dd..affc2b695d 100644 --- a/hmp.h +++ b/hmp.h @@ -98,5 +98,6 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str); void device_add_completion(ReadLineState *rs, int nb_args, const char *str); void device_del_completion(ReadLineState *rs, int nb_args, const char *str); void sendkey_completion(ReadLineState *rs, int nb_args, const char *str); +void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str); #endif diff --git a/monitor.c b/monitor.c index cb9b2c25bb..55e56969d4 100644 --- a/monitor.c +++ b/monitor.c @@ -4339,6 +4339,29 @@ static void device_del_bus_completion(ReadLineState *rs, BusState *bus, } } +void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + ChardevInfoList *list, *start; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev(NULL); + while (list) { + ChardevInfo *chr = list->value; + + if (!strncmp(chr->label, str, len)) { + readline_add_completion(rs, chr->label); + } + list = list->next; + } + qapi_free_ChardevInfoList(start); +} + void device_del_completion(ReadLineState *rs, int nb_args, const char *str) { size_t len; From 13e315dadaaa8fa6d04d10ae762ba63b54ede0ca Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Wed, 7 May 2014 23:41:29 +0100 Subject: [PATCH 16/20] monitor: Add chardev-add backend argument completion. Signed-off-by: Hani Benhabiles Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 1 + hmp.h | 1 + monitor.c | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index ba88e2a183..93fa5347ac 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1623,6 +1623,7 @@ ETEXI .params = "args", .help = "add chardev", .mhandler.cmd = hmp_chardev_add, + .command_completion = chardev_add_completion, }, STEXI diff --git a/hmp.h b/hmp.h index affc2b695d..f8e16a881b 100644 --- a/hmp.h +++ b/hmp.h @@ -99,5 +99,6 @@ void device_add_completion(ReadLineState *rs, int nb_args, const char *str); void device_del_completion(ReadLineState *rs, int nb_args, const char *str); void sendkey_completion(ReadLineState *rs, int nb_args, const char *str); void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str); +void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str); #endif diff --git a/monitor.c b/monitor.c index 55e56969d4..ef5168bf64 100644 --- a/monitor.c +++ b/monitor.c @@ -4269,6 +4269,29 @@ static const char *next_arg_type(const char *typestr) return (p != NULL ? ++p : typestr); } +void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + ChardevBackendInfoList *list, *start; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + + start = list = qmp_query_chardev_backends(NULL); + while (list) { + const char *chr_name = list->value->name; + + if (!strncmp(chr_name, str, len)) { + readline_add_completion(rs, chr_name); + } + list = list->next; + } + qapi_free_ChardevBackendInfoList(start); +} + void device_add_completion(ReadLineState *rs, int nb_args, const char *str) { GSList *list, *elt; From 40d19394b792fb80dba2f536fc13b5b604780a4d Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Wed, 7 May 2014 23:41:30 +0100 Subject: [PATCH 17/20] monitor: Add set_link arguments completion. Make it possible to query all net clients without specifying an ID when calling qemu_find_net_clients_except(). This also adds the add_completion_option() function which is to be used for other commands completions as well. Signed-off-by: Hani Benhabiles Reviewed-by: Stefan Hajnoczi Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 1 + hmp.h | 1 + monitor.c | 34 ++++++++++++++++++++++++++++++++++ net/net.c | 2 +- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 93fa5347ac..3e7b29c99d 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1340,6 +1340,7 @@ ETEXI .params = "name on|off", .help = "change the link status of a network adapter", .mhandler.cmd = hmp_set_link, + .command_completion = set_link_completion, }, STEXI diff --git a/hmp.h b/hmp.h index f8e16a881b..91c9c8539a 100644 --- a/hmp.h +++ b/hmp.h @@ -100,5 +100,6 @@ void device_del_completion(ReadLineState *rs, int nb_args, const char *str); void sendkey_completion(ReadLineState *rs, int nb_args, const char *str); void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str); void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str); +void set_link_completion(ReadLineState *rs, int nb_args, const char *str); #endif diff --git a/monitor.c b/monitor.c index ef5168bf64..fd50c2dd1f 100644 --- a/monitor.c +++ b/monitor.c @@ -4269,6 +4269,17 @@ static const char *next_arg_type(const char *typestr) return (p != NULL ? ++p : typestr); } +static void add_completion_option(ReadLineState *rs, const char *str, + const char *option) +{ + if (!str || !option) { + return; + } + if (!strncmp(option, str, strlen(str))) { + readline_add_completion(rs, option); + } +} + void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) { size_t len; @@ -4444,6 +4455,29 @@ void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) } } +void set_link_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + NetClientState *ncs[255]; + int count, i; + count = qemu_find_net_clients_except(NULL, ncs, + NET_CLIENT_OPTIONS_KIND_NONE, 255); + for (i = 0; i < count; i++) { + const char *name = ncs[i]->name; + if (!strncmp(str, name, len)) { + readline_add_completion(rs, name); + } + } + } else if (nb_args == 3) { + add_completion_option(rs, str, "on"); + add_completion_option(rs, str, "off"); + } +} + static void monitor_find_completion_by_table(Monitor *mon, const mon_cmd_t *cmd_table, char **args, diff --git a/net/net.c b/net/net.c index 9db4dba769..0ff2e40f35 100644 --- a/net/net.c +++ b/net/net.c @@ -633,7 +633,7 @@ int qemu_find_net_clients_except(const char *id, NetClientState **ncs, if (nc->info->type == type) { continue; } - if (!strcmp(nc->name, id)) { + if (!id || !strcmp(nc->name, id)) { if (ret < max) { ncs[ret] = nc; } From b162b49adc4cc6aa2c2ed0a31998f23dfe5983e6 Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Wed, 7 May 2014 23:41:31 +0100 Subject: [PATCH 18/20] monitor: Add netdev_add type argument completion. Also update the command's documentation. Signed-off-by: Hani Benhabiles Reviewed-by: Stefan Hajnoczi Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 3 ++- hmp.h | 1 + monitor.c | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 3e7b29c99d..a7f9b2fd89 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1234,9 +1234,10 @@ ETEXI { .name = "netdev_add", .args_type = "netdev:O", - .params = "[user|tap|socket|hubport|netmap],id=str[,prop=value][,...]", + .params = "[user|tap|socket|vde|bridge|hubport|netmap],id=str[,prop=value][,...]", .help = "add host network device", .mhandler.cmd = hmp_netdev_add, + .command_completion = netdev_add_completion, }, STEXI diff --git a/hmp.h b/hmp.h index 91c9c8539a..bcecd0da16 100644 --- a/hmp.h +++ b/hmp.h @@ -101,5 +101,6 @@ void sendkey_completion(ReadLineState *rs, int nb_args, const char *str); void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str); void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str); void set_link_completion(ReadLineState *rs, int nb_args, const char *str); +void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str); #endif diff --git a/monitor.c b/monitor.c index fd50c2dd1f..a3dd3614fc 100644 --- a/monitor.c +++ b/monitor.c @@ -4303,6 +4303,21 @@ void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) qapi_free_ChardevBackendInfoList(start); } +void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + int i; + + if (nb_args != 2) { + return; + } + len = strlen(str); + readline_set_completion_index(rs, len); + for (i = 0; NetClientOptionsKind_lookup[i]; i++) { + add_completion_option(rs, str, NetClientOptionsKind_lookup[i]); + } +} + void device_add_completion(ReadLineState *rs, int nb_args, const char *str) { GSList *list, *elt; From 11b389f21e4531c23fb8a8474bc8fc7ac2e136a5 Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Wed, 7 May 2014 23:41:32 +0100 Subject: [PATCH 19/20] monitor: Add netdev_del id argument completion. Signed-off-by: Hani Benhabiles Reviewed-by: Stefan Hajnoczi Signed-off-by: Luiz Capitulino --- hmp-commands.hx | 1 + hmp.h | 1 + monitor.c | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index a7f9b2fd89..2e462c04aa 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1252,6 +1252,7 @@ ETEXI .params = "id", .help = "remove host network device", .mhandler.cmd = hmp_netdev_del, + .command_completion = netdev_del_completion, }, STEXI diff --git a/hmp.h b/hmp.h index bcecd0da16..aba59e95f0 100644 --- a/hmp.h +++ b/hmp.h @@ -102,5 +102,6 @@ void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str); void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str); void set_link_completion(ReadLineState *rs, int nb_args, const char *str); void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str); +void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str); #endif diff --git a/monitor.c b/monitor.c index a3dd3614fc..593679a17a 100644 --- a/monitor.c +++ b/monitor.c @@ -4493,6 +4493,32 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str) } } +void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) +{ + int len, count, i; + NetClientState *ncs[255]; + + if (nb_args != 2) { + return; + } + + len = strlen(str); + readline_set_completion_index(rs, len); + count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC, + 255); + for (i = 0; i < count; i++) { + QemuOpts *opts; + const char *name = ncs[i]->name; + if (strncmp(str, name, len)) { + continue; + } + opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name); + if (opts) { + readline_add_completion(rs, name); + } + } +} + static void monitor_find_completion_by_table(Monitor *mon, const mon_cmd_t *cmd_table, char **args, From 24fd848950b44de7e2d71fb69ba52b90d6acb220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Canet?= Date: Fri, 16 May 2014 12:51:56 +0200 Subject: [PATCH 20/20] qapi: skip redundant includes The purpose of this change is to help create a json file containing common definitions; each bit of generated C code must be emitted only one time. A second history global to all QAPISchema instances has been added to detect when a file is included more than one time and skip these includes. It does not act as a stack and the changes made to it by the __init__ function are propagated back to the caller so it's really a global state. Signed-off-by: Benoit Canet Reviewed-by: Eric Blake Signed-off-by: Luiz Capitulino --- docs/qapi-code-gen.txt | 2 +- scripts/qapi.py | 14 +++++++++++--- tests/Makefile | 3 ++- tests/qapi-schema/include-repetition-sub.json | 2 ++ tests/qapi-schema/include-repetition.err | 0 tests/qapi-schema/include-repetition.exit | 1 + tests/qapi-schema/include-repetition.json | 3 +++ tests/qapi-schema/include-repetition.out | 3 +++ 8 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 tests/qapi-schema/include-repetition-sub.json create mode 100644 tests/qapi-schema/include-repetition.err create mode 100644 tests/qapi-schema/include-repetition.exit create mode 100644 tests/qapi-schema/include-repetition.json create mode 100644 tests/qapi-schema/include-repetition.out diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 867d124fe2..dea0d505a7 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -48,7 +48,7 @@ The QAPI schema definitions can be modularized using the 'include' directive: { 'include': 'path/to/file.json'} The directive is evaluated recursively, and include paths are relative to the -file using the directive. +file using the directive. Multiple includes of the same file are safe. === Complex types === diff --git a/scripts/qapi.py b/scripts/qapi.py index ec806aabeb..0265b404dd 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -73,13 +73,18 @@ class QAPIExprError(Exception): class QAPISchema: - def __init__(self, fp, input_relname=None, include_hist=[], parent_info=None): + def __init__(self, fp, input_relname=None, include_hist=[], + previously_included=[], parent_info=None): + """ include_hist is a stack used to detect inclusion cycles + previously_included is a global state used to avoid multiple + inclusions of the same file""" input_fname = os.path.abspath(fp.name) if input_relname is None: input_relname = fp.name self.input_dir = os.path.dirname(input_fname) self.input_file = input_relname self.include_hist = include_hist + [(input_relname, input_fname)] + previously_included.append(input_fname) self.parent_info = parent_info self.src = fp.read() if self.src == '' or self.src[-1] != '\n': @@ -106,13 +111,16 @@ class QAPISchema: for elem in self.include_hist): raise QAPIExprError(expr_info, "Inclusion loop for %s" % include) + # skip multiple include of the same file + if include_path in previously_included: + continue try: fobj = open(include_path, 'r') except IOError as e: raise QAPIExprError(expr_info, '%s: %s' % (e.strerror, include)) - exprs_include = QAPISchema(fobj, include, - self.include_hist, expr_info) + exprs_include = QAPISchema(fobj, include, self.include_hist, + previously_included, expr_info) self.exprs.extend(exprs_include.exprs) else: expr_elem = {'expr': expr, diff --git a/tests/Makefile b/tests/Makefile index 6b8b6f273a..9f7ca61ae3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -193,7 +193,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ flat-union-string-discriminator.json \ include-simple.json include-relpath.json include-format-err.json \ include-non-file.json include-no-file.json include-before-err.json \ - include-nested-err.json include-self-cycle.json include-cycle.json) + include-nested-err.json include-self-cycle.json include-cycle.json \ + include-repetition.json) GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h diff --git a/tests/qapi-schema/include-repetition-sub.json b/tests/qapi-schema/include-repetition-sub.json new file mode 100644 index 0000000000..6bfffdfd55 --- /dev/null +++ b/tests/qapi-schema/include-repetition-sub.json @@ -0,0 +1,2 @@ +{ 'include': 'comments.json' } +{ 'include': 'comments.json' } diff --git a/tests/qapi-schema/include-repetition.err b/tests/qapi-schema/include-repetition.err new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/include-repetition.exit b/tests/qapi-schema/include-repetition.exit new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/tests/qapi-schema/include-repetition.exit @@ -0,0 +1 @@ +0 diff --git a/tests/qapi-schema/include-repetition.json b/tests/qapi-schema/include-repetition.json new file mode 100644 index 0000000000..ec329dde58 --- /dev/null +++ b/tests/qapi-schema/include-repetition.json @@ -0,0 +1,3 @@ +{ 'include': 'comments.json' } +{ 'include': 'include-repetition-sub.json' } +{ 'include': 'comments.json' } diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out new file mode 100644 index 0000000000..4ce3dcf12f --- /dev/null +++ b/tests/qapi-schema/include-repetition.out @@ -0,0 +1,3 @@ +[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])] +[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] +[]