[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v14 13/19] qmp: Tighten output visitor rules
From: |
Markus Armbruster |
Subject: |
Re: [Qemu-devel] [PATCH v14 13/19] qmp: Tighten output visitor rules |
Date: |
Fri, 15 Apr 2016 11:02:31 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) |
Eric Blake <address@hidden> writes:
> Add a new qmp_output_visitor_reset(), which must be called before
> reusing an exising QmpOutputVisitor on a new root object. Tighten
> assertions to require that qmp_output_get_qobject() can only be
> called after pairing a visit_end_* for every visit_start_* (rather
> than allowing it to return a partially built object), and that it
> must not be called unless at least one visit_type_* or visit_start/
> visit_end pair has occurred since creation/reset (the accidental
> return of NULL fixed by commit ab8bf1d7 would have been much
> easier to diagnose).
>
> Also, check that we are encountering the expected object or list
> type (both during visit_end*, and also by validating whether 'name'
> was NULL - although the latter may need to change later if we
> improve error messages by always passing in a sensible name).
> This provides protection against mismatched push(struct)/pop(list)
> or push(list)/pop(struct), similar to the qmp-input protection
> added in commit bdd8e6b5.
>
> Signed-off-by: Eric Blake <address@hidden>
As written, the commit message makes me wonder why we add
qmp_output_visitor_reset() in the same commit. I think the reason is
the tightened rules make it necessary. The commit message could make
that clearer by explaining the rule changes first, then point out we
need a reset to comply with the rules.
>
> ---
> v14: no change
> v13: no change
> v12: rebase to latest, move type_null() into earlier patches,
> don't change signature of pop, don't auto-reset after a single
> get_qobject
> [no v10, v11]
> v9: rebase to added patch, squash in more sanity checks, drop
> Marc-Andre's R-b
> v8: rename qmp_output_reset to qmp_output_visitor_reset
> v7: new patch, based on discussion about spapr_drc.c
> ---
> include/qapi/qmp-output-visitor.h | 1 +
> qapi/qmp-output-visitor.c | 39
> +++++++++++++++++++++++----------------
> tests/test-qmp-output-visitor.c | 6 ++++++
> 3 files changed, 30 insertions(+), 16 deletions(-)
>
> diff --git a/include/qapi/qmp-output-visitor.h
> b/include/qapi/qmp-output-visitor.h
> index 2266770..5093f0d 100644
> --- a/include/qapi/qmp-output-visitor.h
> +++ b/include/qapi/qmp-output-visitor.h
> @@ -21,6 +21,7 @@ typedef struct QmpOutputVisitor QmpOutputVisitor;
>
> QmpOutputVisitor *qmp_output_visitor_new(void);
> void qmp_output_visitor_cleanup(QmpOutputVisitor *v);
> +void qmp_output_visitor_reset(QmpOutputVisitor *v);
>
> QObject *qmp_output_get_qobject(QmpOutputVisitor *v);
> Visitor *qmp_output_get_visitor(QmpOutputVisitor *v);
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 5681ad3..7c48dfb 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -82,9 +82,8 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const
> char *name,
> QObject *cur = e ? e->value : NULL;
>
> if (!cur) {
> - /* FIXME we should require the user to reset the visitor, rather
> - * than throwing away the previous root */
> - qobject_decref(qov->root);
> + /* Don't allow reuse of visitor on more than one root */
> + assert(!qov->root);
> qov->root = value;
> } else {
> switch (qobject_type(cur)) {
> @@ -93,6 +92,9 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov, const
> char *name,
> qdict_put_obj(qobject_to_qdict(cur), name, value);
> break;
> case QTYPE_QLIST:
> + /* FIXME: assertion needs adjustment if we fix visit-core
> + * to pass "name.0" style name during lists. */
visit-core merely passes through whatever name it gets from the client.
Thus, saying we 'fix visit-core to pass "name.0"' is a bit misleading.
What we'd do is change it to require "name.0", then update its users to
comply.
Moreover, this is a note, not a FIXME: nothing is broken here. The
closest we get to "broken" are the bad error messages, but they're
elsewhere.
I'd simply drop the comment.
> + assert(!name);
PATCH 08 made this part of the contract. It also added a bunch of
contract-checking assertions. Should this one be in PATCH 08, too?
> qlist_append_obj(qobject_to_qlist(cur), value);
> break;
> default:
> @@ -114,7 +116,8 @@ static void qmp_output_start_struct(Visitor *v, const
> char *name, void **obj,
> static void qmp_output_end_struct(Visitor *v, Error **errp)
> {
> QmpOutputVisitor *qov = to_qov(v);
> - qmp_output_pop(qov);
> + QObject *value = qmp_output_pop(qov);
> + assert(qobject_type(value) == QTYPE_QDICT);
> }
>
> static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
> @@ -145,7 +148,8 @@ static GenericList *qmp_output_next_list(Visitor *v,
> GenericList **listp,
> static void qmp_output_end_list(Visitor *v)
> {
> QmpOutputVisitor *qov = to_qov(v);
> - qmp_output_pop(qov);
> + QObject *value = qmp_output_pop(qov);
> + assert(qobject_type(value) == QTYPE_QLIST);
> }
>
> static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj,
> @@ -202,18 +206,15 @@ static void qmp_output_type_null(Visitor *v, const char
> *name, Error **errp)
> qmp_output_add_obj(qov, name, qnull());
> }
>
> -/* Finish building, and return the root object. Will not be NULL. */
> +/* Finish building, and return the root object. Will not be NULL.
> + * Caller must use qobject_decref() on the result. */
Well, it must only if it wants the object freed, but I know what you
mean.
Perhaps:
/*
* Finish building, and return the root object.
* The root object is never null.
* The caller becomes the object's owner. It should free it with
* qobject_decref().
*/
> QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
> {
> - /* FIXME: we should require that a visit occurred, and that it is
> - * complete (no starts without a matching end) */
> - QObject *obj = qov->root;
> - if (obj) {
> - qobject_incref(obj);
> - } else {
> - obj = qnull();
> - }
> - return obj;
> + /* A visit must have occurred, with each start paired with end. */
> + assert(qov->root && !QTAILQ_FIRST(&qov->stack));
&& QTAILQ_EMPTY(&qov-stack) would be clearer, wouldn't it?
> +
> + qobject_incref(qov->root);
> + return qov->root;
> }
>
> Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
> @@ -221,7 +222,7 @@ Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
> return &v->visitor;
> }
>
> -void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> +void qmp_output_visitor_reset(QmpOutputVisitor *v)
> {
> QStackEntry *e, *tmp;
>
> @@ -231,6 +232,12 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> }
>
> qobject_decref(v->root);
> + v->root = NULL;
> +}
> +
> +void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
> +{
> + qmp_output_visitor_reset(v);
> g_free(v);
> }
>
> diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
> index e656d99..7be33ba 100644
> --- a/tests/test-qmp-output-visitor.c
> +++ b/tests/test-qmp-output-visitor.c
> @@ -139,6 +139,7 @@ static void test_visitor_out_enum(TestOutputVisitorData
> *data,
> g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
> EnumOne_lookup[i]);
> qobject_decref(obj);
> + qmp_output_visitor_reset(data->qov);
> }
> }
>
> @@ -153,6 +154,7 @@ static void
> test_visitor_out_enum_errors(TestOutputVisitorData *data,
> visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
> g_assert(err);
> error_free(err);
> + qmp_output_visitor_reset(data->qov);
> }
> }
>
> @@ -262,6 +264,7 @@ static void
> test_visitor_out_struct_errors(TestOutputVisitorData *data,
> visit_type_UserDefOne(data->ov, "unused", &pu, &err);
> g_assert(err);
> error_free(err);
> + qmp_output_visitor_reset(data->qov);
> }
> }
>
> @@ -366,6 +369,7 @@ static void test_visitor_out_any(TestOutputVisitorData
> *data,
> qobject_decref(obj);
> qobject_decref(qobj);
>
> + qmp_output_visitor_reset(data->qov);
> qdict = qdict_new();
> qdict_put(qdict, "integer", qint_from_int(-42));
> qdict_put(qdict, "boolean", qbool_from_bool(true));
> @@ -442,6 +446,7 @@ static void
> test_visitor_out_alternate(TestOutputVisitorData *data,
> qapi_free_UserDefAlternate(tmp);
> qobject_decref(arg);
>
> + qmp_output_visitor_reset(data->qov);
> tmp = g_new0(UserDefAlternate, 1);
> tmp->type = QTYPE_QSTRING;
> tmp->u.s = g_strdup("hello");
> @@ -455,6 +460,7 @@ static void
> test_visitor_out_alternate(TestOutputVisitorData *data,
> qapi_free_UserDefAlternate(tmp);
> qobject_decref(arg);
>
> + qmp_output_visitor_reset(data->qov);
> tmp = g_new0(UserDefAlternate, 1);
> tmp->type = QTYPE_QDICT;
> tmp->u.udfu.integer = 1;
How did you find the places that now need qmp_output_visitor_reset()?
[Qemu-devel] [PATCH v14 16/19] qom: Wrap prop visit in visit_start_struct, Eric Blake, 2016/04/08
[Qemu-devel] [PATCH v14 08/19] qapi: Document visitor interfaces, add assertions, Eric Blake, 2016/04/08
[Qemu-devel] [PATCH v14 10/19] qapi: Add visit_type_null() visitor, Eric Blake, 2016/04/08