]> Git Repo - qemu.git/blame - qapi/qobject-input-visitor.c
tests: Cover input visit beyond end of list
[qemu.git] / qapi / qobject-input-visitor.c
CommitLineData
c40cc0a0
MR
1/*
2 * Input Visitor
3 *
08f9541d 4 * Copyright (C) 2012-2016 Red Hat, Inc.
c40cc0a0
MR
5 * Copyright IBM, Corp. 2011
6 *
7 * Authors:
8 * Anthony Liguori <[email protected]>
9 *
10 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
11 * See the COPYING.LIB file in the top-level directory.
12 *
13 */
14
cbf21151 15#include "qemu/osdep.h"
da34e65c 16#include "qapi/error.h"
b3db211f 17#include "qapi/qobject-input-visitor.h"
7b1b5d19 18#include "qapi/visitor-impl.h"
1de7afc9 19#include "qemu/queue.h"
c40cc0a0 20#include "qemu-common.h"
7b1b5d19
PB
21#include "qapi/qmp/types.h"
22#include "qapi/qmp/qerror.h"
c40cc0a0 23
a9fc37f6
MA
24typedef struct StackObject {
25 const char *name; /* Name of @obj in its parent, if any */
26 QObject *obj; /* QDict or QList being visited */
1158bb2a 27 void *qapi; /* sanity check that caller uses same pointer */
b471d012 28
a9fc37f6
MA
29 GHashTable *h; /* If @obj is QDict: unvisited keys */
30 const QListEntry *entry; /* If @obj is QList: unvisited tail */
31 unsigned index; /* If @obj is QList: list index of @entry */
3d344c2a 32
a9fc37f6 33 QSLIST_ENTRY(StackObject) node; /* parent */
c40cc0a0
MR
34} StackObject;
35
a9fc37f6 36struct QObjectInputVisitor {
c40cc0a0 37 Visitor visitor;
b471d012 38
ce140b17
EB
39 /* Root of visit at visitor creation. */
40 QObject *root;
41
42 /* Stack of objects being visited (all entries will be either
43 * QDict or QList). */
3d344c2a 44 QSLIST_HEAD(, StackObject) stack;
b471d012 45
a9fc37f6 46 GString *errname; /* Accumulator for full_name() */
c40cc0a0
MR
47};
48
09e68369 49static QObjectInputVisitor *to_qiv(Visitor *v)
c40cc0a0 50{
09e68369 51 return container_of(v, QObjectInputVisitor, visitor);
c40cc0a0
MR
52}
53
a4a1c70d
MA
54static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name,
55 int n)
a9fc37f6
MA
56{
57 StackObject *so;
58 char buf[32];
59
60 if (qiv->errname) {
61 g_string_truncate(qiv->errname, 0);
62 } else {
63 qiv->errname = g_string_new("");
64 }
65
66 QSLIST_FOREACH(so , &qiv->stack, node) {
a4a1c70d
MA
67 if (n) {
68 n--;
69 } else if (qobject_type(so->obj) == QTYPE_QDICT) {
70 g_string_prepend(qiv->errname, name ?: "<anonymous>");
a9fc37f6
MA
71 g_string_prepend_c(qiv->errname, '.');
72 } else {
73 snprintf(buf, sizeof(buf), "[%u]", so->index);
74 g_string_prepend(qiv->errname, buf);
75 }
76 name = so->name;
77 }
a4a1c70d 78 assert(!n);
a9fc37f6
MA
79
80 if (name) {
81 g_string_prepend(qiv->errname, name);
82 } else if (qiv->errname->str[0] == '.') {
83 g_string_erase(qiv->errname, 0, 1);
a4a1c70d 84 } else if (!qiv->errname->str[0]) {
a9fc37f6
MA
85 return "<anonymous>";
86 }
87
88 return qiv->errname->str;
89}
90
a4a1c70d
MA
91static const char *full_name(QObjectInputVisitor *qiv, const char *name)
92{
93 return full_name_nth(qiv, name, 0);
94}
95
a9fc37f6
MA
96static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv,
97 const char *name,
98 bool consume)
c40cc0a0 99{
ce140b17
EB
100 StackObject *tos;
101 QObject *qobj;
e5826a2f 102 QObject *ret;
b471d012 103
3d344c2a 104 if (QSLIST_EMPTY(&qiv->stack)) {
ce140b17 105 /* Starting at root, name is ignored. */
5d0cbbcf 106 assert(qiv->root);
ce140b17
EB
107 return qiv->root;
108 }
109
110 /* We are in a container; find the next element. */
3d344c2a 111 tos = QSLIST_FIRST(&qiv->stack);
ce140b17 112 qobj = tos->obj;
b471d012
EB
113 assert(qobj);
114
ce140b17
EB
115 if (qobject_type(qobj) == QTYPE_QDICT) {
116 assert(name);
e5826a2f
EB
117 ret = qdict_get(qobject_to_qdict(qobj), name);
118 if (tos->h && consume && ret) {
119 bool removed = g_hash_table_remove(tos->h, name);
120 assert(removed);
47c6d3ec 121 }
ce140b17 122 } else {
b471d012 123 assert(qobject_type(qobj) == QTYPE_QLIST);
ce140b17
EB
124 assert(!name);
125 ret = qlist_entry_obj(tos->entry);
eac8e79f 126 assert(ret);
fcf3cb21
EB
127 if (consume) {
128 tos->entry = qlist_next(tos->entry);
a9fc37f6 129 tos->index++;
fcf3cb21 130 }
c40cc0a0
MR
131 }
132
ce140b17 133 return ret;
c40cc0a0
MR
134}
135
a9fc37f6
MA
136static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
137 const char *name,
138 bool consume, Error **errp)
139{
140 QObject *obj = qobject_input_try_get_object(qiv, name, consume);
141
142 if (!obj) {
143 error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name));
144 }
145 return obj;
146}
147
e38ac962
PB
148static void qdict_add_key(const char *key, QObject *obj, void *opaque)
149{
150 GHashTable *h = opaque;
151 g_hash_table_insert(h, (gpointer) key, NULL);
152}
153
09e68369 154static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
a9fc37f6 155 const char *name,
b8874fbf 156 QObject *obj, void *qapi)
c40cc0a0 157{
e38ac962 158 GHashTable *h;
3d344c2a 159 StackObject *tos = g_new0(StackObject, 1);
c40cc0a0 160
b471d012 161 assert(obj);
a9fc37f6 162 tos->name = name;
b471d012 163 tos->obj = obj;
1158bb2a 164 tos->qapi = qapi;
e38ac962 165
048abb7b 166 if (qobject_type(obj) == QTYPE_QDICT) {
e38ac962
PB
167 h = g_hash_table_new(g_str_hash, g_str_equal);
168 qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
b471d012 169 tos->h = h;
048abb7b
MA
170 } else {
171 assert(qobject_type(obj) == QTYPE_QLIST);
fcf3cb21 172 tos->entry = qlist_first(qobject_to_qlist(obj));
a9fc37f6 173 tos->index = -1;
e38ac962
PB
174 }
175
3d344c2a 176 QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
d9f62dde 177 return tos->entry;
c40cc0a0
MR
178}
179
57a33d89 180
09e68369 181static void qobject_input_check_struct(Visitor *v, Error **errp)
c40cc0a0 182{
09e68369 183 QObjectInputVisitor *qiv = to_qiv(v);
3d344c2a 184 StackObject *tos = QSLIST_FIRST(&qiv->stack);
048abb7b
MA
185 GHashTableIter iter;
186 const char *key;
e38ac962 187
3d344c2a 188 assert(tos && !tos->entry);
048abb7b
MA
189
190 g_hash_table_iter_init(&iter, tos->h);
191 if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
192 error_setg(errp, "Parameter '%s' is unexpected",
193 full_name(qiv, key));
15c2f669
EB
194 }
195}
196
09e68369 197static void qobject_input_stack_object_free(StackObject *tos)
15c2f669 198{
3d344c2a
PB
199 if (tos->h) {
200 g_hash_table_unref(tos->h);
201 }
15c2f669 202
3d344c2a
PB
203 g_free(tos);
204}
15c2f669 205
09e68369 206static void qobject_input_pop(Visitor *v, void **obj)
3d344c2a 207{
09e68369 208 QObjectInputVisitor *qiv = to_qiv(v);
3d344c2a 209 StackObject *tos = QSLIST_FIRST(&qiv->stack);
e38ac962 210
3d344c2a
PB
211 assert(tos && tos->qapi == obj);
212 QSLIST_REMOVE_HEAD(&qiv->stack, node);
09e68369 213 qobject_input_stack_object_free(tos);
c40cc0a0
MR
214}
215
09e68369
DB
216static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
217 size_t size, Error **errp)
c40cc0a0 218{
09e68369
DB
219 QObjectInputVisitor *qiv = to_qiv(v);
220 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
c40cc0a0 221
e58d695e
EB
222 if (obj) {
223 *obj = NULL;
224 }
1382d4ab
MAL
225 if (!qobj) {
226 return;
227 }
228 if (qobject_type(qobj) != QTYPE_QDICT) {
a9fc37f6
MA
229 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
230 full_name(qiv, name), "object");
c40cc0a0
MR
231 return;
232 }
233
a9fc37f6 234 qobject_input_push(qiv, name, qobj, obj);
c40cc0a0
MR
235
236 if (obj) {
7267c094 237 *obj = g_malloc0(size);
c40cc0a0
MR
238 }
239}
240
c40cc0a0 241
09e68369
DB
242static void qobject_input_start_list(Visitor *v, const char *name,
243 GenericList **list, size_t size,
244 Error **errp)
c40cc0a0 245{
09e68369
DB
246 QObjectInputVisitor *qiv = to_qiv(v);
247 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
d9f62dde 248 const QListEntry *entry;
c40cc0a0 249
58561c27
MA
250 if (list) {
251 *list = NULL;
252 }
1382d4ab
MAL
253 if (!qobj) {
254 return;
255 }
256 if (qobject_type(qobj) != QTYPE_QLIST) {
a9fc37f6
MA
257 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
258 full_name(qiv, name), "array");
c40cc0a0
MR
259 return;
260 }
261
a9fc37f6 262 entry = qobject_input_push(qiv, name, qobj, list);
58561c27
MA
263 if (entry && list) {
264 *list = g_malloc0(size);
d9f62dde 265 }
c40cc0a0
MR
266}
267
09e68369
DB
268static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
269 size_t size)
c40cc0a0 270{
09e68369 271 QObjectInputVisitor *qiv = to_qiv(v);
a4a1c70d
MA
272 StackObject *tos = QSLIST_FIRST(&qiv->stack);
273
274 assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
c40cc0a0 275
a4a1c70d 276 if (!tos->entry) {
c40cc0a0
MR
277 return NULL;
278 }
d9f62dde
EB
279 tail->next = g_malloc0(size);
280 return tail->next;
c40cc0a0
MR
281}
282
a4a1c70d
MA
283static void qobject_input_check_list(Visitor *v, Error **errp)
284{
285 QObjectInputVisitor *qiv = to_qiv(v);
286 StackObject *tos = QSLIST_FIRST(&qiv->stack);
287
288 assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
289
290 if (tos->entry) {
291 error_setg(errp, "Only %u list elements expected in %s",
292 tos->index + 1, full_name_nth(qiv, NULL, 1));
293 }
294}
295
c40cc0a0 296
09e68369
DB
297static void qobject_input_start_alternate(Visitor *v, const char *name,
298 GenericAlternate **obj, size_t size,
299 bool promote_int, Error **errp)
69dd62df 300{
09e68369
DB
301 QObjectInputVisitor *qiv = to_qiv(v);
302 QObject *qobj = qobject_input_get_object(qiv, name, false, errp);
69dd62df
KW
303
304 if (!qobj) {
dbf11922 305 *obj = NULL;
69dd62df
KW
306 return;
307 }
dbf11922
EB
308 *obj = g_malloc0(size);
309 (*obj)->type = qobject_type(qobj);
310 if (promote_int && (*obj)->type == QTYPE_QINT) {
311 (*obj)->type = QTYPE_QFLOAT;
d00341af 312 }
69dd62df
KW
313}
314
09e68369
DB
315static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
316 Error **errp)
c40cc0a0 317{
09e68369
DB
318 QObjectInputVisitor *qiv = to_qiv(v);
319 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
1382d4ab 320 QInt *qint;
c40cc0a0 321
1382d4ab
MAL
322 if (!qobj) {
323 return;
324 }
325 qint = qobject_to_qint(qobj);
fcf73f66 326 if (!qint) {
a9fc37f6
MA
327 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
328 full_name(qiv, name), "integer");
c40cc0a0
MR
329 return;
330 }
331
fcf73f66 332 *obj = qint_get_int(qint);
c40cc0a0
MR
333}
334
09e68369
DB
335static void qobject_input_type_uint64(Visitor *v, const char *name,
336 uint64_t *obj, Error **errp)
f755dea7
EB
337{
338 /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
09e68369
DB
339 QObjectInputVisitor *qiv = to_qiv(v);
340 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
1382d4ab 341 QInt *qint;
f755dea7 342
1382d4ab
MAL
343 if (!qobj) {
344 return;
345 }
346 qint = qobject_to_qint(qobj);
f755dea7 347 if (!qint) {
a9fc37f6
MA
348 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
349 full_name(qiv, name), "integer");
f755dea7
EB
350 return;
351 }
352
353 *obj = qint_get_int(qint);
354}
355
09e68369
DB
356static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
357 Error **errp)
c40cc0a0 358{
09e68369
DB
359 QObjectInputVisitor *qiv = to_qiv(v);
360 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
1382d4ab 361 QBool *qbool;
c40cc0a0 362
1382d4ab
MAL
363 if (!qobj) {
364 return;
365 }
366 qbool = qobject_to_qbool(qobj);
14b61600 367 if (!qbool) {
a9fc37f6
MA
368 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
369 full_name(qiv, name), "boolean");
c40cc0a0
MR
370 return;
371 }
372
14b61600 373 *obj = qbool_get_bool(qbool);
c40cc0a0
MR
374}
375
09e68369
DB
376static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
377 Error **errp)
c40cc0a0 378{
09e68369
DB
379 QObjectInputVisitor *qiv = to_qiv(v);
380 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
1382d4ab 381 QString *qstr;
c40cc0a0 382
1382d4ab
MAL
383 *obj = NULL;
384 if (!qobj) {
385 return;
386 }
387 qstr = qobject_to_qstring(qobj);
7f027843 388 if (!qstr) {
a9fc37f6
MA
389 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
390 full_name(qiv, name), "string");
c40cc0a0
MR
391 return;
392 }
393
7f027843 394 *obj = g_strdup(qstring_get_str(qstr));
c40cc0a0
MR
395}
396
09e68369
DB
397static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
398 Error **errp)
c40cc0a0 399{
09e68369
DB
400 QObjectInputVisitor *qiv = to_qiv(v);
401 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
fcf73f66
MA
402 QInt *qint;
403 QFloat *qfloat;
c40cc0a0 404
1382d4ab
MAL
405 if (!qobj) {
406 return;
407 }
fcf73f66
MA
408 qint = qobject_to_qint(qobj);
409 if (qint) {
410 *obj = qint_get_int(qobject_to_qint(qobj));
c40cc0a0
MR
411 return;
412 }
413
fcf73f66
MA
414 qfloat = qobject_to_qfloat(qobj);
415 if (qfloat) {
1ee51876 416 *obj = qfloat_get_double(qobject_to_qfloat(qobj));
fcf73f66 417 return;
1ee51876 418 }
fcf73f66 419
a9fc37f6
MA
420 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
421 full_name(qiv, name), "number");
c40cc0a0
MR
422}
423
09e68369
DB
424static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
425 Error **errp)
28770e05 426{
09e68369
DB
427 QObjectInputVisitor *qiv = to_qiv(v);
428 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
28770e05 429
1382d4ab 430 *obj = NULL;
c4897802 431 if (!qobj) {
c4897802
MAL
432 return;
433 }
434
28770e05
MA
435 qobject_incref(qobj);
436 *obj = qobj;
437}
438
09e68369 439static void qobject_input_type_null(Visitor *v, const char *name, Error **errp)
3bc97fd5 440{
09e68369
DB
441 QObjectInputVisitor *qiv = to_qiv(v);
442 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
3df016f1 443
c4897802 444 if (!qobj) {
c4897802
MAL
445 return;
446 }
447
3df016f1 448 if (qobject_type(qobj) != QTYPE_QNULL) {
a9fc37f6
MA
449 error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
450 full_name(qiv, name), "null");
3df016f1 451 }
3bc97fd5
EB
452}
453
09e68369 454static void qobject_input_optional(Visitor *v, const char *name, bool *present)
c40cc0a0 455{
09e68369 456 QObjectInputVisitor *qiv = to_qiv(v);
a9fc37f6 457 QObject *qobj = qobject_input_try_get_object(qiv, name, false);
c40cc0a0
MR
458
459 if (!qobj) {
460 *present = false;
461 return;
462 }
463
464 *present = true;
465}
466
09e68369 467static void qobject_input_free(Visitor *v)
2c0ef9f4 468{
09e68369 469 QObjectInputVisitor *qiv = to_qiv(v);
a9fc37f6 470
3d344c2a
PB
471 while (!QSLIST_EMPTY(&qiv->stack)) {
472 StackObject *tos = QSLIST_FIRST(&qiv->stack);
473
474 QSLIST_REMOVE_HEAD(&qiv->stack, node);
09e68369 475 qobject_input_stack_object_free(tos);
3d344c2a 476 }
2c0ef9f4 477
b70ce101 478 qobject_decref(qiv->root);
a9fc37f6
MA
479 if (qiv->errname) {
480 g_string_free(qiv->errname, TRUE);
481 }
b70ce101 482 g_free(qiv);
2c0ef9f4
EB
483}
484
048abb7b 485Visitor *qobject_input_visitor_new(QObject *obj)
c40cc0a0 486{
09e68369 487 QObjectInputVisitor *v;
c40cc0a0 488
5d0cbbcf 489 assert(obj);
7267c094 490 v = g_malloc0(sizeof(*v));
c40cc0a0 491
983f52d4 492 v->visitor.type = VISITOR_INPUT;
09e68369
DB
493 v->visitor.start_struct = qobject_input_start_struct;
494 v->visitor.check_struct = qobject_input_check_struct;
495 v->visitor.end_struct = qobject_input_pop;
496 v->visitor.start_list = qobject_input_start_list;
497 v->visitor.next_list = qobject_input_next_list;
a4a1c70d 498 v->visitor.check_list = qobject_input_check_list;
09e68369
DB
499 v->visitor.end_list = qobject_input_pop;
500 v->visitor.start_alternate = qobject_input_start_alternate;
501 v->visitor.type_int64 = qobject_input_type_int64;
502 v->visitor.type_uint64 = qobject_input_type_uint64;
503 v->visitor.type_bool = qobject_input_type_bool;
504 v->visitor.type_str = qobject_input_type_str;
505 v->visitor.type_number = qobject_input_type_number;
506 v->visitor.type_any = qobject_input_type_any;
507 v->visitor.type_null = qobject_input_type_null;
508 v->visitor.optional = qobject_input_optional;
509 v->visitor.free = qobject_input_free;
c40cc0a0 510
ce140b17 511 v->root = obj;
4faaec6a 512 qobject_incref(obj);
c40cc0a0 513
b70ce101 514 return &v->visitor;
c40cc0a0 515}
This page took 0.428549 seconds and 4 git commands to generate.