mirror of
https://github.com/yuzu-mirror/unicorn.git
synced 2025-12-06 07:12:03 +01:00
Don't embed the root of the visit into the stack of current containers being visited. That way, we no longer get confused on whether the first visit of a dictionary is to the dictionary itself or to one of the members of the dictionary, based on whether the caller passed name=NULL; and makes the QMP Input visitor like other visitors where the value of 'name' is now ignored on the root visit. (We may someday want to revisit the rules on what 'name' should be on a top-level visit, rather than just ignoring it; but that would be the topic of another patch). An audit of all qmp_input_visitor_new() call sites shows that there were only two places where callers had previously been visiting to a QDict with a non-NULL name to bypass a call to visit_start_struct(), and those were fixed in prior patches. Backports commit ce140b176920b5b65184020735a3c65ed3e9aeda from qemu
406 lines
10 KiB
C
406 lines
10 KiB
C
/*
|
|
* Input Visitor
|
|
*
|
|
* Copyright IBM, Corp. 2011
|
|
*
|
|
* Authors:
|
|
* Anthony Liguori <aliguori@us.ibm.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.
|
|
*
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qmp-input-visitor.h"
|
|
#include "qapi/visitor-impl.h"
|
|
#include "qemu/queue.h"
|
|
#include "qemu-common.h"
|
|
#include "qapi/qmp/types.h"
|
|
#include "qapi/qmp/qerror.h"
|
|
|
|
#define QIV_STACK_SIZE 1024
|
|
|
|
typedef struct StackObject
|
|
{
|
|
QObject *obj; /* Object being visited */
|
|
|
|
GHashTable *h; /* If obj is dict: unvisited keys */
|
|
const QListEntry *entry; /* If obj is list: unvisited tail */
|
|
} StackObject;
|
|
|
|
struct QmpInputVisitor
|
|
{
|
|
Visitor visitor;
|
|
|
|
/* Root of visit at visitor creation. */
|
|
QObject *root;
|
|
|
|
/* Stack of objects being visited (all entries will be either
|
|
* QDict or QList). */
|
|
StackObject stack[QIV_STACK_SIZE];
|
|
int nb_stack;
|
|
|
|
/* True to reject parse in visit_end_struct() if unvisited keys remain. */
|
|
bool strict;
|
|
};
|
|
|
|
static QmpInputVisitor *to_qiv(Visitor *v)
|
|
{
|
|
return container_of(v, QmpInputVisitor, visitor);
|
|
}
|
|
|
|
static QObject *qmp_input_get_object(QmpInputVisitor *qiv,
|
|
const char *name,
|
|
bool consume)
|
|
{
|
|
StackObject *tos;
|
|
QObject *qobj;
|
|
QObject *ret;
|
|
|
|
if (!qiv->nb_stack) {
|
|
/* Starting at root, name is ignored. */
|
|
return qiv->root;
|
|
}
|
|
|
|
/* We are in a container; find the next element. */
|
|
tos = &qiv->stack[qiv->nb_stack - 1];
|
|
qobj = tos->obj;
|
|
assert(qobj);
|
|
|
|
if (qobject_type(qobj) == QTYPE_QDICT) {
|
|
assert(name);
|
|
ret = qdict_get(qobject_to_qdict(qobj), name);
|
|
if (tos->h && consume && ret) {
|
|
bool removed = g_hash_table_remove(tos->h, name);
|
|
assert(removed);
|
|
}
|
|
} else {
|
|
assert(qobject_type(qobj) == QTYPE_QLIST);
|
|
assert(!name);
|
|
ret = qlist_entry_obj(tos->entry);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void qdict_add_key(const char *key, QObject *obj, void *opaque)
|
|
{
|
|
GHashTable *h = opaque;
|
|
g_hash_table_insert(h, (gpointer) key, NULL);
|
|
}
|
|
|
|
static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
|
|
{
|
|
GHashTable *h;
|
|
StackObject *tos = &qiv->stack[qiv->nb_stack];
|
|
|
|
assert(obj);
|
|
if (qiv->nb_stack >= QIV_STACK_SIZE) {
|
|
error_setg(errp, "An internal buffer overran");
|
|
return;
|
|
}
|
|
|
|
tos->obj = obj;
|
|
tos->entry = NULL;
|
|
tos->h = NULL;
|
|
|
|
if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
|
|
h = g_hash_table_new(g_str_hash, g_str_equal);
|
|
qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
|
|
tos->h = h;
|
|
}
|
|
|
|
qiv->nb_stack++;
|
|
}
|
|
|
|
/** Only for qmp_input_pop. */
|
|
static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey)
|
|
{
|
|
*(const char **)user_pkey = (const char *)key;
|
|
return TRUE;
|
|
}
|
|
|
|
static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp)
|
|
{
|
|
assert(qiv->nb_stack > 0);
|
|
|
|
if (qiv->strict) {
|
|
GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h;
|
|
if (top_ht) {
|
|
if (g_hash_table_size(top_ht)) {
|
|
const char *key;
|
|
g_hash_table_find(top_ht, always_true, (gpointer)&key);
|
|
error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
|
|
}
|
|
g_hash_table_unref(top_ht);
|
|
}
|
|
}
|
|
|
|
qiv->nb_stack--;
|
|
}
|
|
|
|
static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
|
|
size_t size, Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QObject *qobj = qmp_input_get_object(qiv, name, false);
|
|
Error *err = NULL;
|
|
|
|
if (obj) {
|
|
*obj = NULL;
|
|
}
|
|
if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"QDict");
|
|
return;
|
|
}
|
|
|
|
qmp_input_push(qiv, qobj, &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
|
|
if (obj) {
|
|
*obj = g_malloc0(size);
|
|
}
|
|
}
|
|
|
|
static void qmp_input_end_struct(Visitor *v, Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
|
|
qmp_input_pop(qiv, &error_abort);
|
|
}
|
|
|
|
static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
|
|
size_t size, Error **errp)
|
|
{
|
|
if (obj) {
|
|
*obj = g_malloc0(size);
|
|
}
|
|
}
|
|
|
|
static void qmp_input_end_implicit_struct(Visitor *v)
|
|
{
|
|
}
|
|
|
|
static void qmp_input_start_list(Visitor *v, const char *name, Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
|
|
|
if (!qobj || qobject_type(qobj) != QTYPE_QLIST) {
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"list");
|
|
return;
|
|
}
|
|
|
|
qmp_input_push(qiv, qobj, errp);
|
|
}
|
|
|
|
static GenericList *qmp_input_next_list(Visitor *v, GenericList **list,
|
|
size_t size)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
GenericList *entry;
|
|
StackObject *so = &qiv->stack[qiv->nb_stack - 1];
|
|
bool first;
|
|
|
|
if (so->entry == NULL) {
|
|
so->entry = qlist_first(qobject_to_qlist(so->obj));
|
|
first = true;
|
|
} else {
|
|
so->entry = qlist_next(so->entry);
|
|
first = false;
|
|
}
|
|
|
|
if (so->entry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
entry = g_malloc0(size);
|
|
if (first) {
|
|
*list = entry;
|
|
} else {
|
|
(*list)->next = entry;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
static void qmp_input_end_list(Visitor *v)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
|
|
qmp_input_pop(qiv, &error_abort);
|
|
}
|
|
|
|
static void qmp_input_get_next_type(Visitor *v, const char *name, QType *type,
|
|
bool promote_int, Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QObject *qobj = qmp_input_get_object(qiv, name, false);
|
|
|
|
if (!qobj) {
|
|
error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
|
|
return;
|
|
}
|
|
if (promote_int && *type == QTYPE_QINT) {
|
|
*type = QTYPE_QFLOAT;
|
|
}
|
|
*type = qobject_type(qobj);
|
|
}
|
|
|
|
static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj,
|
|
Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
|
|
|
|
if (!qint) {
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"integer");
|
|
return;
|
|
}
|
|
|
|
*obj = qint_get_int(qint);
|
|
}
|
|
|
|
static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
|
Error **errp)
|
|
{
|
|
/* FIXME: qobject_to_qint mishandles values over INT64_MAX */
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QInt *qint = qobject_to_qint(qmp_input_get_object(qiv, name, true));
|
|
|
|
if (!qint) {
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"integer");
|
|
return;
|
|
}
|
|
|
|
*obj = qint_get_int(qint);
|
|
}
|
|
|
|
static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj,
|
|
Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QBool *qbool = qobject_to_qbool(qmp_input_get_object(qiv, name, true));
|
|
|
|
if (!qbool) {
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"boolean");
|
|
return;
|
|
}
|
|
|
|
*obj = qbool_get_bool(qbool);
|
|
}
|
|
|
|
static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
|
|
Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
|
|
|
|
if (!qstr) {
|
|
*obj = NULL;
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"string");
|
|
return;
|
|
}
|
|
|
|
*obj = g_strdup(qstring_get_str(qstr));
|
|
}
|
|
|
|
static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
|
|
Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
|
QInt *qint;
|
|
QFloat *qfloat;
|
|
|
|
qint = qobject_to_qint(qobj);
|
|
if (qint) {
|
|
*obj = qint_get_int(qobject_to_qint(qobj));
|
|
return;
|
|
}
|
|
|
|
qfloat = qobject_to_qfloat(qobj);
|
|
if (qfloat) {
|
|
*obj = qfloat_get_double(qobject_to_qfloat(qobj));
|
|
return;
|
|
}
|
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"number");
|
|
}
|
|
|
|
static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj,
|
|
Error **errp)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
|
|
|
qobject_incref(qobj);
|
|
*obj = qobj;
|
|
}
|
|
|
|
static void qmp_input_optional(Visitor *v, const char *name, bool *present)
|
|
{
|
|
QmpInputVisitor *qiv = to_qiv(v);
|
|
QObject *qobj = qmp_input_get_object(qiv, name, true);
|
|
|
|
if (!qobj) {
|
|
*present = false;
|
|
return;
|
|
}
|
|
|
|
*present = true;
|
|
}
|
|
|
|
Visitor *qmp_input_get_visitor(QmpInputVisitor *v)
|
|
{
|
|
return &v->visitor;
|
|
}
|
|
|
|
void qmp_input_visitor_cleanup(QmpInputVisitor *v)
|
|
{
|
|
qobject_decref(v->root);
|
|
g_free(v);
|
|
}
|
|
|
|
QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict)
|
|
{
|
|
QmpInputVisitor *v;
|
|
|
|
v = g_malloc0(sizeof(*v));
|
|
|
|
v->visitor.type = VISITOR_INPUT;
|
|
v->visitor.start_struct = qmp_input_start_struct;
|
|
v->visitor.end_struct = qmp_input_end_struct;
|
|
v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
|
|
v->visitor.end_implicit_struct = qmp_input_end_implicit_struct;
|
|
v->visitor.start_list = qmp_input_start_list;
|
|
v->visitor.next_list = qmp_input_next_list;
|
|
v->visitor.end_list = qmp_input_end_list;
|
|
v->visitor.type_int64 = qmp_input_type_int64;
|
|
v->visitor.type_uint64 = qmp_input_type_uint64;
|
|
v->visitor.type_bool = qmp_input_type_bool;
|
|
v->visitor.type_str = qmp_input_type_str;
|
|
v->visitor.type_number = qmp_input_type_number;
|
|
v->visitor.type_any = qmp_input_type_any;
|
|
v->visitor.optional = qmp_input_optional;
|
|
v->strict = strict;
|
|
v->visitor.get_next_type = qmp_input_get_next_type;
|
|
|
|
v->root = obj;
|
|
qobject_incref(obj);
|
|
|
|
return v;
|
|
}
|