Implement a visitor that frees the pointer members of the visited QAPI object in the same way that qapi_dealloc_visitor does, but similarly to qobject_input_visitor, takes an input QObject that will dictate which members get freed and which don't. Members not present in the input QObject will be left unchanged in the visited QAPI object.
This is useful to free memory just before perfoming a visit with qobject_input_visitor on a pre-existing, non-null QAPI object. If the same QObject is passed to both visitors, the pointers overwritten by the input visitor match the ones that are freed by the dealloc visitor. Signed-off-by: Fabiano Rosas <[email protected]> --- include/qapi/dealloc-visitor.h | 6 ++ qapi/qapi-dealloc-visitor.c | 173 ++++++++++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 1 deletion(-) diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h index c36715fdf3..96c7bf35c3 100644 --- a/include/qapi/dealloc-visitor.h +++ b/include/qapi/dealloc-visitor.h @@ -25,4 +25,10 @@ typedef struct QapiDeallocVisitor QapiDeallocVisitor; */ Visitor *qapi_dealloc_visitor_new(void); +/* + * Like qapi_dealloc_visitor_new but visits a QObject and only frees + * present members. + */ +Visitor *qapi_dealloc_present_visitor_new(QObject *); + #endif diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index 57a2c904bb..90b017cc93 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -14,14 +14,146 @@ #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" +#include "qemu/queue.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qobject/qnull.h" #include "qapi/visitor-impl.h" +typedef struct QStackEntry { + QObject *obj; /* QDict or QList being visited */ + void *qapi; + const QListEntry *entry; /* If @obj is QList: unvisited tail */ + QSLIST_ENTRY(QStackEntry) node; +} QStackEntry; + struct QapiDeallocVisitor { Visitor visitor; + QObject *root; + QSLIST_HEAD(, QStackEntry) stack; }; +static void qapi_dealloc_pop(Visitor *v, void **obj) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QStackEntry *se = QSLIST_FIRST(&qdv->stack); + + assert(se && se->qapi == obj); + QSLIST_REMOVE_HEAD(&qdv->stack, node); + g_free(se); +} + +static void qapi_dealloc_push(Visitor *v, QObject *obj, void *qapi) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QStackEntry *se = g_new0(QStackEntry, 1); + + assert(obj); + se->obj = obj; + se->qapi = qapi; + + if (qobject_type(obj) == QTYPE_QLIST) { + se->entry = qlist_first(qobject_to(QList, obj)); + } + + QSLIST_INSERT_HEAD(&qdv->stack, se, node); +} + +static QObject *qapi_dealloc_try_get_object(QapiDeallocVisitor *qdv, const char *name) +{ + QStackEntry *se = QSLIST_FIRST(&qdv->stack); + QObject *qobj; + QObject *ret = NULL; + + if (!se) { + assert(qdv->root); + return qdv->root; + } + + qobj = se->obj; + assert(qobj); + + if (qobject_type(qobj) == QTYPE_QDICT) { + assert(name); + ret = qdict_get(qobject_to(QDict, qobj), name); + } else { + assert(qobject_type(qobj) == QTYPE_QLIST); + assert(!name); + if (se->entry) { + ret = qlist_entry_obj(se->entry); + } + } + + return ret; +} + +static bool qapi_dealloc_present_start_struct(Visitor *v, const char *name, + void **obj, size_t size, + Error **errp) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QObject *qobj = qapi_dealloc_try_get_object(qdv, name); + + if (!qobj) { + return false; + } + assert(qobject_type(qobj) == QTYPE_QDICT); + qapi_dealloc_push(v, qobj, obj); + return true; +} + +static void qapi_dealloc_present_end_struct(Visitor *v, void **obj) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QStackEntry *se = QSLIST_FIRST(&qdv->stack); + + assert(qobject_type(se->obj) == QTYPE_QDICT); + qapi_dealloc_pop(v, obj); + + if (obj) { + g_free(*obj); + } +} + +static bool qapi_dealloc_present_start_list(Visitor *v, const char *name, + GenericList **list, size_t size, + Error **errp) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QObject *qobj = qapi_dealloc_try_get_object(qdv, name); + + if (!qobj) { + return false; + } + assert(qobject_type(qobj) == QTYPE_QLIST); + qapi_dealloc_push(v, qobj, list); + return true; +} + +static void qapi_dealloc_present_end_list(Visitor *v, void **obj) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QStackEntry *se = QSLIST_FIRST(&qdv->stack); + + assert(qobject_type(se->obj) == QTYPE_QLIST); + qapi_dealloc_pop(v, obj); +} + +static void qapi_dealloc_present_free(Visitor *v) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + + while (!QSLIST_EMPTY(&qdv->stack)) { + QStackEntry *se = QSLIST_FIRST(&qdv->stack); + + QSLIST_REMOVE_HEAD(&qdv->stack, node); + g_free(se); + } + qobject_unref(qdv->root); + g_free(qdv); +} + static bool qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj, size_t unused, Error **errp) { @@ -35,6 +167,21 @@ static void qapi_dealloc_end_struct(Visitor *v, void **obj) } } +static bool qapi_dealloc_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + Error **errp) +{ + QapiDeallocVisitor *qdv = container_of(v, QapiDeallocVisitor, visitor); + QObject *qobj = qapi_dealloc_try_get_object(qdv, name); + + if (!qobj) { + return false; + } + assert(*obj); + (*obj)->type = qobject_type(qobj); + return true; +} + static void qapi_dealloc_end_alternate(Visitor *v, void **obj) { if (obj) { @@ -117,13 +264,14 @@ static void qapi_dealloc_free(Visitor *v) g_free(container_of(v, QapiDeallocVisitor, visitor)); } -Visitor *qapi_dealloc_visitor_new(void) +static QapiDeallocVisitor *qapi_dealloc_visitor_new_base(void) { QapiDeallocVisitor *v; v = g_malloc0(sizeof(*v)); v->visitor.type = VISITOR_DEALLOC; + v->visitor.start_struct = qapi_dealloc_start_struct; v->visitor.end_struct = qapi_dealloc_end_struct; v->visitor.end_alternate = qapi_dealloc_end_alternate; @@ -139,5 +287,28 @@ Visitor *qapi_dealloc_visitor_new(void) v->visitor.type_null = qapi_dealloc_type_null; v->visitor.free = qapi_dealloc_free; + return v; +} + +Visitor *qapi_dealloc_visitor_new(void) +{ + QapiDeallocVisitor *v = qapi_dealloc_visitor_new_base(); + + return &v->visitor; +} + +Visitor *qapi_dealloc_present_visitor_new(QObject *obj) +{ + QapiDeallocVisitor *v = qapi_dealloc_visitor_new_base(); + + v->visitor.start_alternate = qapi_dealloc_start_alternate; + v->visitor.start_list = qapi_dealloc_present_start_list; + v->visitor.end_list = qapi_dealloc_present_end_list; + v->visitor.start_struct = qapi_dealloc_present_start_struct; + v->visitor.end_struct = qapi_dealloc_present_end_struct; + v->visitor.free = qapi_dealloc_present_free; + + v->root = qobject_ref(obj); + return &v->visitor; } -- 2.51.0
