qemu/qapi/qmp-output-visitor.c
<<
>>
Prefs
   1/*
   2 * Core Definitions for QAPI/QMP Command Registry
   3 *
   4 * Copyright IBM, Corp. 2011
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  10 * See the COPYING.LIB file in the top-level directory.
  11 *
  12 */
  13
  14#include "qapi/qmp-output-visitor.h"
  15#include "qapi/visitor-impl.h"
  16#include "qemu/queue.h"
  17#include "qemu-common.h"
  18#include "qapi/qmp/types.h"
  19
  20typedef struct QStackEntry
  21{
  22    QObject *value;
  23    bool is_list_head;
  24    QTAILQ_ENTRY(QStackEntry) node;
  25} QStackEntry;
  26
  27typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
  28
  29struct QmpOutputVisitor
  30{
  31    Visitor visitor;
  32    /* FIXME: we are abusing stack to hold two separate pieces of
  33     * information: the current root object in slot 0, and the stack
  34     * of N objects still being built in slots 1 through N (for N+1
  35     * slots in use).  Worse, our behavior is inconsistent:
  36     * qmp_output_add_obj() visiting two top-level scalars in a row
  37     * discards the first in favor of the second, but visiting two
  38     * top-level objects in a row tries to append the second object
  39     * into the first (since the first object was placed in the stack
  40     * in both slot 0 and 1, but only popped from slot 1).  */
  41    QStack stack;
  42};
  43
  44#define qmp_output_add(qov, name, value) \
  45    qmp_output_add_obj(qov, name, QOBJECT(value))
  46#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value))
  47
  48static QmpOutputVisitor *to_qov(Visitor *v)
  49{
  50    return container_of(v, QmpOutputVisitor, visitor);
  51}
  52
  53/* Push @value onto the stack of current QObjects being built */
  54static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
  55{
  56    QStackEntry *e = g_malloc0(sizeof(*e));
  57
  58    assert(value);
  59    e->value = value;
  60    if (qobject_type(e->value) == QTYPE_QLIST) {
  61        e->is_list_head = true;
  62    }
  63    QTAILQ_INSERT_HEAD(&qov->stack, e, node);
  64}
  65
  66/* Pop a value off the stack of QObjects being built, and return it. */
  67static QObject *qmp_output_pop(QmpOutputVisitor *qov)
  68{
  69    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
  70    QObject *value;
  71
  72    assert(e);
  73    QTAILQ_REMOVE(&qov->stack, e, node);
  74    value = e->value;
  75    assert(value);
  76    g_free(e);
  77    return value;
  78}
  79
  80/* Grab the root QObject, if any */
  81static QObject *qmp_output_first(QmpOutputVisitor *qov)
  82{
  83    QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
  84
  85    if (!e) {
  86        /* No root */
  87        return NULL;
  88    }
  89    assert(e->value);
  90    return e->value;
  91}
  92
  93/* Peek at the top of the stack of QObjects being built.
  94 * The stack must not be empty. */
  95static QObject *qmp_output_last(QmpOutputVisitor *qov)
  96{
  97    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
  98
  99    assert(e && e->value);
 100    return e->value;
 101}
 102
 103/* Add @value to the current QObject being built.
 104 * If the stack is visiting a dictionary or list, @value is now owned
 105 * by that container. Otherwise, @value is now the root.  */
 106static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
 107                               QObject *value)
 108{
 109    QObject *cur;
 110
 111    if (QTAILQ_EMPTY(&qov->stack)) {
 112        /* Stack was empty, track this object as root */
 113        qmp_output_push_obj(qov, value);
 114        return;
 115    }
 116
 117    cur = qmp_output_last(qov);
 118
 119    switch (qobject_type(cur)) {
 120    case QTYPE_QDICT:
 121        assert(name);
 122        qdict_put_obj(qobject_to_qdict(cur), name, value);
 123        break;
 124    case QTYPE_QLIST:
 125        qlist_append_obj(qobject_to_qlist(cur), value);
 126        break;
 127    default:
 128        /* The previous root was a scalar, replace it with a new root */
 129        /* FIXME this is abusing the stack; see comment above */
 130        qobject_decref(qmp_output_pop(qov));
 131        assert(QTAILQ_EMPTY(&qov->stack));
 132        qmp_output_push_obj(qov, value);
 133        break;
 134    }
 135}
 136
 137static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind,
 138                                    const char *name, size_t unused,
 139                                    Error **errp)
 140{
 141    QmpOutputVisitor *qov = to_qov(v);
 142    QDict *dict = qdict_new();
 143
 144    qmp_output_add(qov, name, dict);
 145    qmp_output_push(qov, dict);
 146}
 147
 148static void qmp_output_end_struct(Visitor *v, Error **errp)
 149{
 150    QmpOutputVisitor *qov = to_qov(v);
 151    qmp_output_pop(qov);
 152}
 153
 154static void qmp_output_start_list(Visitor *v, const char *name, Error **errp)
 155{
 156    QmpOutputVisitor *qov = to_qov(v);
 157    QList *list = qlist_new();
 158
 159    qmp_output_add(qov, name, list);
 160    qmp_output_push(qov, list);
 161}
 162
 163static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp,
 164                                         Error **errp)
 165{
 166    GenericList *list = *listp;
 167    QmpOutputVisitor *qov = to_qov(v);
 168    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
 169
 170    assert(e);
 171    if (e->is_list_head) {
 172        e->is_list_head = false;
 173        return list;
 174    }
 175
 176    return list ? list->next : NULL;
 177}
 178
 179static void qmp_output_end_list(Visitor *v, Error **errp)
 180{
 181    QmpOutputVisitor *qov = to_qov(v);
 182    qmp_output_pop(qov);
 183}
 184
 185static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name,
 186                                Error **errp)
 187{
 188    QmpOutputVisitor *qov = to_qov(v);
 189    qmp_output_add(qov, name, qint_from_int(*obj));
 190}
 191
 192static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name,
 193                                 Error **errp)
 194{
 195    QmpOutputVisitor *qov = to_qov(v);
 196    qmp_output_add(qov, name, qbool_from_bool(*obj));
 197}
 198
 199static void qmp_output_type_str(Visitor *v, char **obj, const char *name,
 200                                Error **errp)
 201{
 202    QmpOutputVisitor *qov = to_qov(v);
 203    if (*obj) {
 204        qmp_output_add(qov, name, qstring_from_str(*obj));
 205    } else {
 206        qmp_output_add(qov, name, qstring_from_str(""));
 207    }
 208}
 209
 210static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
 211                                   Error **errp)
 212{
 213    QmpOutputVisitor *qov = to_qov(v);
 214    qmp_output_add(qov, name, qfloat_from_double(*obj));
 215}
 216
 217static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
 218                                Error **errp)
 219{
 220    QmpOutputVisitor *qov = to_qov(v);
 221    qobject_incref(*obj);
 222    qmp_output_add_obj(qov, name, *obj);
 223}
 224
 225/* Finish building, and return the root object. Will not be NULL. */
 226QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
 227{
 228    QObject *obj = qmp_output_first(qov);
 229    if (obj) {
 230        qobject_incref(obj);
 231    } else {
 232        obj = qnull();
 233    }
 234    return obj;
 235}
 236
 237Visitor *qmp_output_get_visitor(QmpOutputVisitor *v)
 238{
 239    return &v->visitor;
 240}
 241
 242void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
 243{
 244    QStackEntry *e, *tmp;
 245
 246    /* The bottom QStackEntry, if any, owns the root QObject. See the
 247     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
 248    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
 249
 250    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
 251        QTAILQ_REMOVE(&v->stack, e, node);
 252        g_free(e);
 253    }
 254
 255    qobject_decref(root);
 256    g_free(v);
 257}
 258
 259QmpOutputVisitor *qmp_output_visitor_new(void)
 260{
 261    QmpOutputVisitor *v;
 262
 263    v = g_malloc0(sizeof(*v));
 264
 265    v->visitor.start_struct = qmp_output_start_struct;
 266    v->visitor.end_struct = qmp_output_end_struct;
 267    v->visitor.start_list = qmp_output_start_list;
 268    v->visitor.next_list = qmp_output_next_list;
 269    v->visitor.end_list = qmp_output_end_list;
 270    v->visitor.type_enum = output_type_enum;
 271    v->visitor.type_int = qmp_output_type_int;
 272    v->visitor.type_bool = qmp_output_type_bool;
 273    v->visitor.type_str = qmp_output_type_str;
 274    v->visitor.type_number = qmp_output_type_number;
 275    v->visitor.type_any = qmp_output_type_any;
 276
 277    QTAILQ_INIT(&v->stack);
 278
 279    return v;
 280}
 281