qemu-devel
[Top][All Lists]
Advanced

[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()?



reply via email to

[Prev in Thread] Current Thread [Next in Thread]