]> git.proxmox.com Git - mirror_qemu.git/blob - qapi/qobject-input-visitor.c
qapi: Make QObject input visitor set *list reliably
[mirror_qemu.git] / qapi / qobject-input-visitor.c
1 /*
2 * Input Visitor
3 *
4 * Copyright (C) 2012-2016 Red Hat, Inc.
5 * Copyright IBM, Corp. 2011
6 *
7 * Authors:
8 * Anthony Liguori <aliguori@us.ibm.com>
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
15 #include "qemu/osdep.h"
16 #include "qapi/error.h"
17 #include "qapi/qobject-input-visitor.h"
18 #include "qapi/visitor-impl.h"
19 #include "qemu/queue.h"
20 #include "qemu-common.h"
21 #include "qapi/qmp/types.h"
22 #include "qapi/qmp/qerror.h"
23
24 typedef struct StackObject
25 {
26 QObject *obj; /* Object being visited */
27 void *qapi; /* sanity check that caller uses same pointer */
28
29 GHashTable *h; /* If obj is dict: unvisited keys */
30 const QListEntry *entry; /* If obj is list: unvisited tail */
31
32 QSLIST_ENTRY(StackObject) node;
33 } StackObject;
34
35 struct QObjectInputVisitor
36 {
37 Visitor visitor;
38
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). */
44 QSLIST_HEAD(, StackObject) stack;
45
46 /* True to reject parse in visit_end_struct() if unvisited keys remain. */
47 bool strict;
48 };
49
50 static QObjectInputVisitor *to_qiv(Visitor *v)
51 {
52 return container_of(v, QObjectInputVisitor, visitor);
53 }
54
55 static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
56 const char *name,
57 bool consume, Error **errp)
58 {
59 StackObject *tos;
60 QObject *qobj;
61 QObject *ret;
62
63 if (QSLIST_EMPTY(&qiv->stack)) {
64 /* Starting at root, name is ignored. */
65 assert(qiv->root);
66 return qiv->root;
67 }
68
69 /* We are in a container; find the next element. */
70 tos = QSLIST_FIRST(&qiv->stack);
71 qobj = tos->obj;
72 assert(qobj);
73
74 if (qobject_type(qobj) == QTYPE_QDICT) {
75 assert(name);
76 ret = qdict_get(qobject_to_qdict(qobj), name);
77 if (tos->h && consume && ret) {
78 bool removed = g_hash_table_remove(tos->h, name);
79 assert(removed);
80 }
81 if (!ret) {
82 error_setg(errp, QERR_MISSING_PARAMETER, name);
83 }
84 } else {
85 assert(qobject_type(qobj) == QTYPE_QLIST);
86 assert(!name);
87 ret = qlist_entry_obj(tos->entry);
88 assert(ret);
89 if (consume) {
90 tos->entry = qlist_next(tos->entry);
91 }
92 }
93
94 return ret;
95 }
96
97 static void qdict_add_key(const char *key, QObject *obj, void *opaque)
98 {
99 GHashTable *h = opaque;
100 g_hash_table_insert(h, (gpointer) key, NULL);
101 }
102
103 static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
104 QObject *obj, void *qapi)
105 {
106 GHashTable *h;
107 StackObject *tos = g_new0(StackObject, 1);
108
109 assert(obj);
110 tos->obj = obj;
111 tos->qapi = qapi;
112
113 if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
114 h = g_hash_table_new(g_str_hash, g_str_equal);
115 qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
116 tos->h = h;
117 } else if (qobject_type(obj) == QTYPE_QLIST) {
118 tos->entry = qlist_first(qobject_to_qlist(obj));
119 }
120
121 QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
122 return tos->entry;
123 }
124
125
126 static void qobject_input_check_struct(Visitor *v, Error **errp)
127 {
128 QObjectInputVisitor *qiv = to_qiv(v);
129 StackObject *tos = QSLIST_FIRST(&qiv->stack);
130
131 assert(tos && !tos->entry);
132 if (qiv->strict) {
133 GHashTable *const top_ht = tos->h;
134 if (top_ht) {
135 GHashTableIter iter;
136 const char *key;
137
138 g_hash_table_iter_init(&iter, top_ht);
139 if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
140 error_setg(errp, "Parameter '%s' is unexpected", key);
141 }
142 }
143 }
144 }
145
146 static void qobject_input_stack_object_free(StackObject *tos)
147 {
148 if (tos->h) {
149 g_hash_table_unref(tos->h);
150 }
151
152 g_free(tos);
153 }
154
155 static void qobject_input_pop(Visitor *v, void **obj)
156 {
157 QObjectInputVisitor *qiv = to_qiv(v);
158 StackObject *tos = QSLIST_FIRST(&qiv->stack);
159
160 assert(tos && tos->qapi == obj);
161 QSLIST_REMOVE_HEAD(&qiv->stack, node);
162 qobject_input_stack_object_free(tos);
163 }
164
165 static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
166 size_t size, Error **errp)
167 {
168 QObjectInputVisitor *qiv = to_qiv(v);
169 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
170
171 if (obj) {
172 *obj = NULL;
173 }
174 if (!qobj) {
175 return;
176 }
177 if (qobject_type(qobj) != QTYPE_QDICT) {
178 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
179 "QDict");
180 return;
181 }
182
183 qobject_input_push(qiv, qobj, obj);
184
185 if (obj) {
186 *obj = g_malloc0(size);
187 }
188 }
189
190
191 static void qobject_input_start_list(Visitor *v, const char *name,
192 GenericList **list, size_t size,
193 Error **errp)
194 {
195 QObjectInputVisitor *qiv = to_qiv(v);
196 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
197 const QListEntry *entry;
198
199 if (list) {
200 *list = NULL;
201 }
202 if (!qobj) {
203 return;
204 }
205 if (qobject_type(qobj) != QTYPE_QLIST) {
206 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
207 "list");
208 return;
209 }
210
211 entry = qobject_input_push(qiv, qobj, list);
212 if (entry && list) {
213 *list = g_malloc0(size);
214 }
215 }
216
217 static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
218 size_t size)
219 {
220 QObjectInputVisitor *qiv = to_qiv(v);
221 StackObject *so = QSLIST_FIRST(&qiv->stack);
222
223 if (!so->entry) {
224 return NULL;
225 }
226 tail->next = g_malloc0(size);
227 return tail->next;
228 }
229
230
231 static void qobject_input_start_alternate(Visitor *v, const char *name,
232 GenericAlternate **obj, size_t size,
233 bool promote_int, Error **errp)
234 {
235 QObjectInputVisitor *qiv = to_qiv(v);
236 QObject *qobj = qobject_input_get_object(qiv, name, false, errp);
237
238 if (!qobj) {
239 *obj = NULL;
240 return;
241 }
242 *obj = g_malloc0(size);
243 (*obj)->type = qobject_type(qobj);
244 if (promote_int && (*obj)->type == QTYPE_QINT) {
245 (*obj)->type = QTYPE_QFLOAT;
246 }
247 }
248
249 static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
250 Error **errp)
251 {
252 QObjectInputVisitor *qiv = to_qiv(v);
253 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
254 QInt *qint;
255
256 if (!qobj) {
257 return;
258 }
259 qint = qobject_to_qint(qobj);
260 if (!qint) {
261 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
262 "integer");
263 return;
264 }
265
266 *obj = qint_get_int(qint);
267 }
268
269 static void qobject_input_type_uint64(Visitor *v, const char *name,
270 uint64_t *obj, Error **errp)
271 {
272 /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
273 QObjectInputVisitor *qiv = to_qiv(v);
274 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
275 QInt *qint;
276
277 if (!qobj) {
278 return;
279 }
280 qint = qobject_to_qint(qobj);
281 if (!qint) {
282 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
283 "integer");
284 return;
285 }
286
287 *obj = qint_get_int(qint);
288 }
289
290 static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
291 Error **errp)
292 {
293 QObjectInputVisitor *qiv = to_qiv(v);
294 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
295 QBool *qbool;
296
297 if (!qobj) {
298 return;
299 }
300 qbool = qobject_to_qbool(qobj);
301 if (!qbool) {
302 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
303 "boolean");
304 return;
305 }
306
307 *obj = qbool_get_bool(qbool);
308 }
309
310 static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
311 Error **errp)
312 {
313 QObjectInputVisitor *qiv = to_qiv(v);
314 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
315 QString *qstr;
316
317 *obj = NULL;
318 if (!qobj) {
319 return;
320 }
321 qstr = qobject_to_qstring(qobj);
322 if (!qstr) {
323 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
324 "string");
325 return;
326 }
327
328 *obj = g_strdup(qstring_get_str(qstr));
329 }
330
331 static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
332 Error **errp)
333 {
334 QObjectInputVisitor *qiv = to_qiv(v);
335 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
336 QInt *qint;
337 QFloat *qfloat;
338
339 if (!qobj) {
340 return;
341 }
342 qint = qobject_to_qint(qobj);
343 if (qint) {
344 *obj = qint_get_int(qobject_to_qint(qobj));
345 return;
346 }
347
348 qfloat = qobject_to_qfloat(qobj);
349 if (qfloat) {
350 *obj = qfloat_get_double(qobject_to_qfloat(qobj));
351 return;
352 }
353
354 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
355 "number");
356 }
357
358 static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
359 Error **errp)
360 {
361 QObjectInputVisitor *qiv = to_qiv(v);
362 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
363
364 *obj = NULL;
365 if (!qobj) {
366 return;
367 }
368
369 qobject_incref(qobj);
370 *obj = qobj;
371 }
372
373 static void qobject_input_type_null(Visitor *v, const char *name, Error **errp)
374 {
375 QObjectInputVisitor *qiv = to_qiv(v);
376 QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
377
378 if (!qobj) {
379 return;
380 }
381
382 if (qobject_type(qobj) != QTYPE_QNULL) {
383 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
384 "null");
385 }
386 }
387
388 static void qobject_input_optional(Visitor *v, const char *name, bool *present)
389 {
390 QObjectInputVisitor *qiv = to_qiv(v);
391 QObject *qobj = qobject_input_get_object(qiv, name, false, NULL);
392
393 if (!qobj) {
394 *present = false;
395 return;
396 }
397
398 *present = true;
399 }
400
401 static void qobject_input_free(Visitor *v)
402 {
403 QObjectInputVisitor *qiv = to_qiv(v);
404 while (!QSLIST_EMPTY(&qiv->stack)) {
405 StackObject *tos = QSLIST_FIRST(&qiv->stack);
406
407 QSLIST_REMOVE_HEAD(&qiv->stack, node);
408 qobject_input_stack_object_free(tos);
409 }
410
411 qobject_decref(qiv->root);
412 g_free(qiv);
413 }
414
415 Visitor *qobject_input_visitor_new(QObject *obj, bool strict)
416 {
417 QObjectInputVisitor *v;
418
419 assert(obj);
420 v = g_malloc0(sizeof(*v));
421
422 v->visitor.type = VISITOR_INPUT;
423 v->visitor.start_struct = qobject_input_start_struct;
424 v->visitor.check_struct = qobject_input_check_struct;
425 v->visitor.end_struct = qobject_input_pop;
426 v->visitor.start_list = qobject_input_start_list;
427 v->visitor.next_list = qobject_input_next_list;
428 v->visitor.end_list = qobject_input_pop;
429 v->visitor.start_alternate = qobject_input_start_alternate;
430 v->visitor.type_int64 = qobject_input_type_int64;
431 v->visitor.type_uint64 = qobject_input_type_uint64;
432 v->visitor.type_bool = qobject_input_type_bool;
433 v->visitor.type_str = qobject_input_type_str;
434 v->visitor.type_number = qobject_input_type_number;
435 v->visitor.type_any = qobject_input_type_any;
436 v->visitor.type_null = qobject_input_type_null;
437 v->visitor.optional = qobject_input_optional;
438 v->visitor.free = qobject_input_free;
439 v->strict = strict;
440
441 v->root = obj;
442 qobject_incref(obj);
443
444 return &v->visitor;
445 }