unicorn/qemu/include/qapi/visitor-impl.h

117 lines
3.8 KiB
C
Raw Normal View History

2015-08-21 07:04:50 +00:00
/*
* Core Definitions for QAPI Visitor implementations
*
* Copyright (C) 2012-2016 Red Hat, Inc.
2015-08-21 07:04:50 +00:00
*
* Author: Paolo Bonizni <pbonzini@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#ifndef QAPI_VISITOR_IMPL_H
#define QAPI_VISITOR_IMPL_H
#include "qapi/error.h"
#include "qapi/visitor.h"
/*
* This file describes the callback interface for implementing a QAPI
* visitor. For the client interface, see visitor.h. When
* implementing the callbacks, it is easiest to declare a struct with
* 'Visitor visitor;' as the first member. A callback's contract
* matches the corresponding public functions' contract unless stated
* otherwise. In the comments below, some callbacks are marked "must
* be set for $TYPE visits to work"; if a visitor implementation omits
* that callback, it should also document that it is only useful for a
* subset of QAPI.
*/
/*
* There are three classes of visitors; setting the class determines
* how QAPI enums are visited, as well as what additional restrictions
* can be asserted.
*/
typedef enum VisitorType {
VISITOR_INPUT,
VISITOR_OUTPUT,
VISITOR_DEALLOC,
} VisitorType;
2015-08-21 07:04:50 +00:00
struct Visitor
{
/* Must be set to visit structs */
void (*start_struct)(Visitor *v, const char *name, void **obj,
size_t size, Error **errp);
qapi: Split visit_end_struct() into pieces As mentioned in previous patches, we want to call visit_end_struct() functions unconditionally, so that visitors can release resources tied up since the matching visit_start_struct() without also having to worry about error priority if more than one error occurs. Even though error_propagate() can be safely used to ignore a second error during cleanup caused by a first error, it is simpler if the cleanup cannot set an error. So, split out the error checking portion (basically, input visitors checking for unvisited keys) into a new function visit_check_struct(), which can be safely skipped if any earlier errors are encountered, and leave the cleanup portion (which never fails, but must be called unconditionally if visit_start_struct() succeeded) in visit_end_struct(). Generated code in qapi-visit.c has diffs resembling: |@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v, | goto out_obj; | } | visit_type_ACPIOSTInfo_members(v, obj, &err); |- error_propagate(errp, err); |- err = NULL; |+ if (err) { |+ goto out_obj; |+ } |+ visit_check_struct(v, &err); | out_obj: |- visit_end_struct(v, &err); |+ visit_end_struct(v); | out: and in qapi-event.c: @@ -47,7 +47,10 @@ void qapi_event_send_acpi_device_ost(ACP | goto out; | } | visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, &param, &err); |- visit_end_struct(v, err ? NULL : &err); |+ if (!err) { |+ visit_check_struct(v, &err); |+ } |+ visit_end_struct(v); | if (err) { | goto out; Backports commit 15c2f669e3fb2bc97f7b42d1871f595c0ac24af8 from qemu
2018-02-24 00:12:23 +00:00
/* Optional; intended for input visitors */
void (*check_struct)(Visitor *v, Error **errp);
/* Must be set to visit structs */
void (*end_struct)(Visitor *v, void **obj);
2015-08-21 07:04:50 +00:00
qapi: Simplify semantics of visit_next_list() The semantics of the list visit are somewhat baroque, with the following pseudocode when FooList is used: start() for (prev = head; cur = next(prev); prev = &cur) { visit(&cur->value) } Note that these semantics (advance before visit) requires that the first call to next() return the list head, while all other calls return the next element of the list; that is, every visitor implementation is required to track extra state to decide whether to return the input as-is, or to advance. It also requires an argument of 'GenericList **' to next(), solely because the first iteration might need to modify the caller's GenericList head, so that all other calls have to do a layer of dereferencing. Thankfully, we only have two uses of list visits in the entire code base: one in spapr_drc (which completely avoids visit_next_list(), feeding in integers from a different source than uint8List), and one in qapi-visit.py. That is, all other list visitors are generated in qapi-visit.c, and share the same paradigm based on a qapi FooList type, so we can refactor how lists are laid out with minimal churn among clients. We can greatly simplify things by hoisting the special case into the start() routine, and flipping the order in the loop to visit before advance: start(head) for (tail = *head; tail; tail = next(tail)) { visit(&tail->value) } With the simpler semantics, visitors have less state to track, the argument to next() is reduced to 'GenericList *', and it also becomes obvious whether an input visitor is allocating a FooList during visit_start_list() (rather than the old way of not knowing if an allocation happened until the first visit_next_list()). As a minor drawback, we now allocate in two functions instead of one, and have to pass the size to both functions (unless we were to tweak the input visitors to cache the size to start_list for reuse during next_list, but that defeats the goal of less visitor state). The signature of visit_start_list() is chosen to match visit_start_struct(), with the new parameters after 'name'. The spapr_drc case is a virtual visit, done by passing NULL for list, similarly to how NULL is passed to visit_start_struct() when a qapi type is not used in those visits. It was easy to provide these semantics for qmp-output and dealloc visitors, and a bit harder for qmp-input (several prerequisite patches refactored things to make this patch straightforward). But it turned out that the string and opts visitors munge enough other state during visit_next_list() to make it easier to just document and require a GenericList visit for now; an assertion will remind us to adjust things if we need the semantics in the future. Several pre-requisite cleanup patches made the reshuffling of the various visitors easier; particularly the qmp input visitor. Backports commit d9f62dde1303286b24ac8ce88be27e2b9b9c5f46 from qemu
2018-02-24 00:44:26 +00:00
/* Must be set; implementations may require @list to be non-null,
* but must document it. */
void (*start_list)(Visitor *v, const char *name, GenericList **list,
size_t size, Error **errp);
/* Must be set */
qapi: Simplify semantics of visit_next_list() The semantics of the list visit are somewhat baroque, with the following pseudocode when FooList is used: start() for (prev = head; cur = next(prev); prev = &cur) { visit(&cur->value) } Note that these semantics (advance before visit) requires that the first call to next() return the list head, while all other calls return the next element of the list; that is, every visitor implementation is required to track extra state to decide whether to return the input as-is, or to advance. It also requires an argument of 'GenericList **' to next(), solely because the first iteration might need to modify the caller's GenericList head, so that all other calls have to do a layer of dereferencing. Thankfully, we only have two uses of list visits in the entire code base: one in spapr_drc (which completely avoids visit_next_list(), feeding in integers from a different source than uint8List), and one in qapi-visit.py. That is, all other list visitors are generated in qapi-visit.c, and share the same paradigm based on a qapi FooList type, so we can refactor how lists are laid out with minimal churn among clients. We can greatly simplify things by hoisting the special case into the start() routine, and flipping the order in the loop to visit before advance: start(head) for (tail = *head; tail; tail = next(tail)) { visit(&tail->value) } With the simpler semantics, visitors have less state to track, the argument to next() is reduced to 'GenericList *', and it also becomes obvious whether an input visitor is allocating a FooList during visit_start_list() (rather than the old way of not knowing if an allocation happened until the first visit_next_list()). As a minor drawback, we now allocate in two functions instead of one, and have to pass the size to both functions (unless we were to tweak the input visitors to cache the size to start_list for reuse during next_list, but that defeats the goal of less visitor state). The signature of visit_start_list() is chosen to match visit_start_struct(), with the new parameters after 'name'. The spapr_drc case is a virtual visit, done by passing NULL for list, similarly to how NULL is passed to visit_start_struct() when a qapi type is not used in those visits. It was easy to provide these semantics for qmp-output and dealloc visitors, and a bit harder for qmp-input (several prerequisite patches refactored things to make this patch straightforward). But it turned out that the string and opts visitors munge enough other state during visit_next_list() to make it easier to just document and require a GenericList visit for now; an assertion will remind us to adjust things if we need the semantics in the future. Several pre-requisite cleanup patches made the reshuffling of the various visitors easier; particularly the qmp input visitor. Backports commit d9f62dde1303286b24ac8ce88be27e2b9b9c5f46 from qemu
2018-02-24 00:44:26 +00:00
GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);
/* Must be set */
void (*end_list)(Visitor *v, void **list);
2015-08-21 07:04:50 +00:00
/* Must be set by input and dealloc visitors to visit alternates;
* optional for output visitors. */
void (*start_alternate)(Visitor *v, const char *name,
GenericAlternate **obj, size_t size,
bool promote_int, Error **errp);
/* Optional, needed for dealloc visitor */
void (*end_alternate)(Visitor *v, void **obj);
/* Must be set */
void (*type_int64)(Visitor *v, const char *name, int64_t *obj,
Error **errp);
/* Must be set */
void (*type_uint64)(Visitor *v, const char *name, uint64_t *obj,
Error **errp);
/* Optional; fallback is type_uint64() */
void (*type_size)(Visitor *v, const char *name, uint64_t *obj,
Error **errp);
/* Must be set */
void (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp);
/* Must be set */
void (*type_str)(Visitor *v, const char *name, char **obj, Error **errp);
/* Must be set to visit numbers */
void (*type_number)(Visitor *v, const char *name, double *obj,
2015-08-21 07:04:50 +00:00
Error **errp);
/* Must be set to visit arbitrary QTypes */
void (*type_any)(Visitor *v, const char *name, QObject **obj,
Error **errp);
2015-08-21 07:04:50 +00:00
/* Must be set to visit explicit null values. */
void (*type_null)(Visitor *v, const char *name, Error **errp);
/* Must be set for input visitors, optional otherwise. The core
* takes care of the return type in the public interface. */
void (*optional)(Visitor *v, const char *name, bool *present);
2015-08-21 07:04:50 +00:00
/* Must be set */
VisitorType type;
qapi: Add new visit_complete() function Making each output visitor provide its own output collection function was the only remaining reason for exposing visitor sub-types to the rest of the code base. Add a polymorphic visit_complete() function which is a no-op for input visitors, and which populates an opaque pointer for output visitors. For maximum type-safety, also add a parameter to the output visitor constructors with a type-correct version of the output pointer, and assert that the two uses match. This approach was considered superior to either passing the output parameter only during construction (action at a distance during visit_free() feels awkward) or only during visit_complete() (defeating type safety makes it easier to use incorrectly). Most callers were function-local, and therefore a mechanical conversion; the testsuite was a bit trickier, but the previous cleanup patch minimized the churn here. The visit_complete() function may be called at most once; doing so lets us use transfer semantics rather than duplication or ref-count semantics to get the just-built output back to the caller, even though it means our behavior is not idempotent. Generated code is simplified as follows for events: |@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP | QDict *qmp; | Error *err = NULL; | QMPEventFuncEmit emit; |- QmpOutputVisitor *qov; |+ QObject *obj; | Visitor *v; | q_obj_ACPI_DEVICE_OST_arg param = { | info |@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP | | qmp = qmp_event_build_dict("ACPI_DEVICE_OST"); | |- qov = qmp_output_visitor_new(); |- v = qmp_output_get_visitor(qov); |+ v = qmp_output_visitor_new(&obj); | | visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err); | if (err) { |@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP | goto out; | } | |- qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov)); |+ visit_complete(v, &obj); |+ qdict_put_obj(qmp, "data", obj); | emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err); and for commands: | { | Error *err = NULL; |- QmpOutputVisitor *qov = qmp_output_visitor_new(); | Visitor *v; | |- v = qmp_output_get_visitor(qov); |+ v = qmp_output_visitor_new(ret_out); | visit_type_AddfdInfo(v, "unused", &ret_in, &err); |- if (err) { |- goto out; |+ if (!err) { |+ visit_complete(v, ret_out); | } |- *ret_out = qmp_output_get_qobject(qov); |- |-out: | error_propagate(errp, err); Backports commit 3b098d56979d2f7fd707c5be85555d114353a28d from qemu
2018-02-25 06:20:00 +00:00
/* Must be set for output visitors, optional otherwise. */
void (*complete)(Visitor *v, void *opaque);
/* Must be set */
void (*free)(Visitor *v);
};
2015-08-21 07:04:50 +00:00
#endif