xref: /qemu/qapi/qobject-input-visitor.c (revision e0ddabc6)
1 /*
2  * Input Visitor
3  *
4  * Copyright (C) 2012-2017 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 <math.h>
17 #include "qapi/compat-policy.h"
18 #include "qapi/error.h"
19 #include "qapi/qobject-input-visitor.h"
20 #include "qapi/visitor-impl.h"
21 #include "qemu/queue.h"
22 #include "qapi/qmp/qjson.h"
23 #include "qapi/qmp/qbool.h"
24 #include "qapi/qmp/qdict.h"
25 #include "qapi/qmp/qerror.h"
26 #include "qapi/qmp/qlist.h"
27 #include "qapi/qmp/qnull.h"
28 #include "qapi/qmp/qnum.h"
29 #include "qapi/qmp/qstring.h"
30 #include "qemu/cutils.h"
31 #include "qemu/keyval.h"
32 
33 typedef struct StackObject {
34     const char *name;            /* Name of @obj in its parent, if any */
35     QObject *obj;                /* QDict or QList being visited */
36     void *qapi; /* sanity check that caller uses same pointer */
37 
38     GHashTable *h;              /* If @obj is QDict: unvisited keys */
39     const QListEntry *entry;    /* If @obj is QList: unvisited tail */
40     unsigned index;             /* If @obj is QList: list index of @entry */
41 
42     QSLIST_ENTRY(StackObject) node; /* parent */
43 } StackObject;
44 
45 struct QObjectInputVisitor {
46     Visitor visitor;
47 
48     /* Root of visit at visitor creation. */
49     QObject *root;
50     bool keyval;                /* Assume @root made with keyval_parse() */
51 
52     /* Stack of objects being visited (all entries will be either
53      * QDict or QList). */
54     QSLIST_HEAD(, StackObject) stack;
55 
56     GString *errname;           /* Accumulator for full_name() */
57 };
58 
59 static QObjectInputVisitor *to_qiv(Visitor *v)
60 {
61     return container_of(v, QObjectInputVisitor, visitor);
62 }
63 
64 /*
65  * Find the full name of something @qiv is currently visiting.
66  * @qiv is visiting something named @name in the stack of containers
67  * @qiv->stack.
68  * If @n is zero, return its full name.
69  * If @n is positive, return the full name of the @n-th container
70  * counting from the top.  The stack of containers must have at least
71  * @n elements.
72  * The returned string is valid until the next full_name_nth(@v) or
73  * destruction of @v.
74  */
75 static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name,
76                                  int n)
77 {
78     StackObject *so;
79     char buf[32];
80 
81     if (qiv->errname) {
82         g_string_truncate(qiv->errname, 0);
83     } else {
84         qiv->errname = g_string_new("");
85     }
86 
87     QSLIST_FOREACH(so , &qiv->stack, node) {
88         if (n) {
89             n--;
90         } else if (qobject_type(so->obj) == QTYPE_QDICT) {
91             g_string_prepend(qiv->errname, name ?: "<anonymous>");
92             g_string_prepend_c(qiv->errname, '.');
93         } else {
94             snprintf(buf, sizeof(buf),
95                      qiv->keyval ? ".%u" : "[%u]",
96                      so->index);
97             g_string_prepend(qiv->errname, buf);
98         }
99         name = so->name;
100     }
101     assert(!n);
102 
103     if (name) {
104         g_string_prepend(qiv->errname, name);
105     } else if (qiv->errname->str[0] == '.') {
106         g_string_erase(qiv->errname, 0, 1);
107     } else if (!qiv->errname->str[0]) {
108         return "<anonymous>";
109     }
110 
111     return qiv->errname->str;
112 }
113 
114 static const char *full_name(QObjectInputVisitor *qiv, const char *name)
115 {
116     return full_name_nth(qiv, name, 0);
117 }
118 
119 static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv,
120                                              const char *name,
121                                              bool consume)
122 {
123     StackObject *tos;
124     QObject *qobj;
125     QObject *ret;
126 
127     if (QSLIST_EMPTY(&qiv->stack)) {
128         /* Starting at root, name is ignored. */
129         assert(qiv->root);
130         return qiv->root;
131     }
132 
133     /* We are in a container; find the next element. */
134     tos = QSLIST_FIRST(&qiv->stack);
135     qobj = tos->obj;
136     assert(qobj);
137 
138     if (qobject_type(qobj) == QTYPE_QDICT) {
139         assert(name);
140         ret = qdict_get(qobject_to(QDict, qobj), name);
141         if (tos->h && consume && ret) {
142             bool removed = g_hash_table_remove(tos->h, name);
143             assert(removed);
144         }
145     } else {
146         assert(qobject_type(qobj) == QTYPE_QLIST);
147         assert(!name);
148         if (tos->entry) {
149             ret = qlist_entry_obj(tos->entry);
150             if (consume) {
151                 tos->entry = qlist_next(tos->entry);
152             }
153         } else {
154             ret = NULL;
155         }
156         if (consume) {
157             tos->index++;
158         }
159     }
160 
161     return ret;
162 }
163 
164 static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
165                                          const char *name,
166                                          bool consume, Error **errp)
167 {
168     QObject *obj = qobject_input_try_get_object(qiv, name, consume);
169 
170     if (!obj) {
171         error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name));
172     }
173     return obj;
174 }
175 
176 static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv,
177                                             const char *name,
178                                             Error **errp)
179 {
180     QObject *qobj;
181     QString *qstr;
182 
183     qobj = qobject_input_get_object(qiv, name, true, errp);
184     if (!qobj) {
185         return NULL;
186     }
187 
188     qstr = qobject_to(QString, qobj);
189     if (!qstr) {
190         switch (qobject_type(qobj)) {
191         case QTYPE_QDICT:
192         case QTYPE_QLIST:
193             error_setg(errp, "Parameters '%s.*' are unexpected",
194                        full_name(qiv, name));
195             return NULL;
196         default:
197             /* Non-string scalar (should this be an assertion?) */
198             error_setg(errp, "Internal error: parameter %s invalid",
199                        full_name(qiv, name));
200             return NULL;
201         }
202     }
203 
204     return qstring_get_str(qstr);
205 }
206 
207 static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
208                                             const char *name,
209                                             QObject *obj, void *qapi)
210 {
211     GHashTable *h;
212     StackObject *tos = g_new0(StackObject, 1);
213     QDict *qdict = qobject_to(QDict, obj);
214     QList *qlist = qobject_to(QList, obj);
215     const QDictEntry *entry;
216 
217     assert(obj);
218     tos->name = name;
219     tos->obj = obj;
220     tos->qapi = qapi;
221 
222     if (qdict) {
223         h = g_hash_table_new(g_str_hash, g_str_equal);
224         for (entry = qdict_first(qdict);
225              entry;
226              entry = qdict_next(qdict, entry)) {
227             g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL);
228         }
229         tos->h = h;
230     } else {
231         assert(qlist);
232         tos->entry = qlist_first(qlist);
233         tos->index = -1;
234     }
235 
236     QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
237     return tos->entry;
238 }
239 
240 
241 static bool qobject_input_check_struct(Visitor *v, Error **errp)
242 {
243     QObjectInputVisitor *qiv = to_qiv(v);
244     StackObject *tos = QSLIST_FIRST(&qiv->stack);
245     GHashTableIter iter;
246     const char *key;
247 
248     assert(tos && !tos->entry);
249 
250     g_hash_table_iter_init(&iter, tos->h);
251     if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
252         error_setg(errp, "Parameter '%s' is unexpected",
253                    full_name(qiv, key));
254         return false;
255     }
256     return true;
257 }
258 
259 static void qobject_input_stack_object_free(StackObject *tos)
260 {
261     if (tos->h) {
262         g_hash_table_unref(tos->h);
263     }
264 
265     g_free(tos);
266 }
267 
268 static void qobject_input_pop(Visitor *v, void **obj)
269 {
270     QObjectInputVisitor *qiv = to_qiv(v);
271     StackObject *tos = QSLIST_FIRST(&qiv->stack);
272 
273     assert(tos && tos->qapi == obj);
274     QSLIST_REMOVE_HEAD(&qiv->stack, node);
275     qobject_input_stack_object_free(tos);
276 }
277 
278 static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj,
279                                        size_t size, Error **errp)
280 {
281     QObjectInputVisitor *qiv = to_qiv(v);
282     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
283 
284     if (obj) {
285         *obj = NULL;
286     }
287     if (!qobj) {
288         return false;
289     }
290     if (qobject_type(qobj) != QTYPE_QDICT) {
291         error_setg(errp, "Invalid parameter type for '%s', expected: object",
292                    full_name(qiv, name));
293         return false;
294     }
295 
296     qobject_input_push(qiv, name, qobj, obj);
297 
298     if (obj) {
299         *obj = g_malloc0(size);
300     }
301     return true;
302 }
303 
304 static void qobject_input_end_struct(Visitor *v, void **obj)
305 {
306     QObjectInputVisitor *qiv = to_qiv(v);
307     StackObject *tos = QSLIST_FIRST(&qiv->stack);
308 
309     assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h);
310     qobject_input_pop(v, obj);
311 }
312 
313 
314 static bool qobject_input_start_list(Visitor *v, const char *name,
315                                      GenericList **list, size_t size,
316                                      Error **errp)
317 {
318     QObjectInputVisitor *qiv = to_qiv(v);
319     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
320     const QListEntry *entry;
321 
322     if (list) {
323         *list = NULL;
324     }
325     if (!qobj) {
326         return false;
327     }
328     if (qobject_type(qobj) != QTYPE_QLIST) {
329         error_setg(errp, "Invalid parameter type for '%s', expected: array",
330                    full_name(qiv, name));
331         return false;
332     }
333 
334     entry = qobject_input_push(qiv, name, qobj, list);
335     if (entry && list) {
336         *list = g_malloc0(size);
337     }
338     return true;
339 }
340 
341 static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
342                                             size_t size)
343 {
344     QObjectInputVisitor *qiv = to_qiv(v);
345     StackObject *tos = QSLIST_FIRST(&qiv->stack);
346 
347     assert(tos && qobject_to(QList, tos->obj));
348 
349     if (!tos->entry) {
350         return NULL;
351     }
352     tail->next = g_malloc0(size);
353     return tail->next;
354 }
355 
356 static bool qobject_input_check_list(Visitor *v, Error **errp)
357 {
358     QObjectInputVisitor *qiv = to_qiv(v);
359     StackObject *tos = QSLIST_FIRST(&qiv->stack);
360 
361     assert(tos && qobject_to(QList, tos->obj));
362 
363     if (tos->entry) {
364         error_setg(errp, "Only %u list elements expected in %s",
365                    tos->index + 1, full_name_nth(qiv, NULL, 1));
366         return false;
367     }
368     return true;
369 }
370 
371 static void qobject_input_end_list(Visitor *v, void **obj)
372 {
373     QObjectInputVisitor *qiv = to_qiv(v);
374     StackObject *tos = QSLIST_FIRST(&qiv->stack);
375 
376     assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h);
377     qobject_input_pop(v, obj);
378 }
379 
380 static bool qobject_input_start_alternate(Visitor *v, const char *name,
381                                           GenericAlternate **obj, size_t size,
382                                           Error **errp)
383 {
384     QObjectInputVisitor *qiv = to_qiv(v);
385     QObject *qobj = qobject_input_get_object(qiv, name, false, errp);
386 
387     if (!qobj) {
388         *obj = NULL;
389         return false;
390     }
391     *obj = g_malloc0(size);
392     (*obj)->type = qobject_type(qobj);
393     return true;
394 }
395 
396 static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
397                                      Error **errp)
398 {
399     QObjectInputVisitor *qiv = to_qiv(v);
400     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
401     QNum *qnum;
402 
403     if (!qobj) {
404         return false;
405     }
406     qnum = qobject_to(QNum, qobj);
407     if (!qnum || !qnum_get_try_int(qnum, obj)) {
408         error_setg(errp, "Invalid parameter type for '%s', expected: integer",
409                    full_name(qiv, name));
410         return false;
411     }
412     return true;
413 }
414 
415 static bool qobject_input_type_int64_keyval(Visitor *v, const char *name,
416                                             int64_t *obj, Error **errp)
417 {
418     QObjectInputVisitor *qiv = to_qiv(v);
419     const char *str = qobject_input_get_keyval(qiv, name, errp);
420 
421     if (!str) {
422         return false;
423     }
424 
425     if (qemu_strtoi64(str, NULL, 0, obj) < 0) {
426         /* TODO report -ERANGE more nicely */
427         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
428                    full_name(qiv, name), "integer");
429         return false;
430     }
431     return true;
432 }
433 
434 static bool qobject_input_type_uint64(Visitor *v, const char *name,
435                                       uint64_t *obj, Error **errp)
436 {
437     QObjectInputVisitor *qiv = to_qiv(v);
438     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
439     QNum *qnum;
440     int64_t val;
441 
442     if (!qobj) {
443         return false;
444     }
445     qnum = qobject_to(QNum, qobj);
446     if (!qnum) {
447         goto err;
448     }
449 
450     if (qnum_get_try_uint(qnum, obj)) {
451         return true;
452     }
453 
454     /* Need to accept negative values for backward compatibility */
455     if (qnum_get_try_int(qnum, &val)) {
456         *obj = val;
457         return true;
458     }
459 
460 err:
461     error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
462                full_name(qiv, name), "uint64");
463     return false;
464 }
465 
466 static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name,
467                                              uint64_t *obj, Error **errp)
468 {
469     QObjectInputVisitor *qiv = to_qiv(v);
470     const char *str = qobject_input_get_keyval(qiv, name, errp);
471 
472     if (!str) {
473         return false;
474     }
475 
476     if (qemu_strtou64(str, NULL, 0, obj) < 0) {
477         /* TODO report -ERANGE more nicely */
478         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
479                    full_name(qiv, name), "integer");
480         return false;
481     }
482     return true;
483 }
484 
485 static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
486                                     Error **errp)
487 {
488     QObjectInputVisitor *qiv = to_qiv(v);
489     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
490     QBool *qbool;
491 
492     if (!qobj) {
493         return false;
494     }
495     qbool = qobject_to(QBool, qobj);
496     if (!qbool) {
497         error_setg(errp, "Invalid parameter type for '%s', expected: boolean",
498                    full_name(qiv, name));
499         return false;
500     }
501 
502     *obj = qbool_get_bool(qbool);
503     return true;
504 }
505 
506 static bool qobject_input_type_bool_keyval(Visitor *v, const char *name,
507                                            bool *obj, Error **errp)
508 {
509     QObjectInputVisitor *qiv = to_qiv(v);
510     const char *str = qobject_input_get_keyval(qiv, name, errp);
511 
512     if (!str) {
513         return false;
514     }
515 
516     if (!qapi_bool_parse(name, str, obj, NULL)) {
517         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
518                    full_name(qiv, name), "'on' or 'off'");
519         return false;
520     }
521     return true;
522 }
523 
524 static bool qobject_input_type_str(Visitor *v, const char *name, char **obj,
525                                    Error **errp)
526 {
527     QObjectInputVisitor *qiv = to_qiv(v);
528     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
529     QString *qstr;
530 
531     *obj = NULL;
532     if (!qobj) {
533         return false;
534     }
535     qstr = qobject_to(QString, qobj);
536     if (!qstr) {
537         error_setg(errp, "Invalid parameter type for '%s', expected: string",
538                    full_name(qiv, name));
539         return false;
540     }
541 
542     *obj = g_strdup(qstring_get_str(qstr));
543     return true;
544 }
545 
546 static bool qobject_input_type_str_keyval(Visitor *v, const char *name,
547                                           char **obj, Error **errp)
548 {
549     QObjectInputVisitor *qiv = to_qiv(v);
550     const char *str = qobject_input_get_keyval(qiv, name, errp);
551 
552     *obj = g_strdup(str);
553     return !!str;
554 }
555 
556 static bool qobject_input_type_number(Visitor *v, const char *name, double *obj,
557                                       Error **errp)
558 {
559     QObjectInputVisitor *qiv = to_qiv(v);
560     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
561     QNum *qnum;
562 
563     if (!qobj) {
564         return false;
565     }
566     qnum = qobject_to(QNum, qobj);
567     if (!qnum) {
568         error_setg(errp, "Invalid parameter type for '%s', expected: number",
569                    full_name(qiv, name));
570         return false;
571     }
572 
573     *obj = qnum_get_double(qnum);
574     return true;
575 }
576 
577 static bool qobject_input_type_number_keyval(Visitor *v, const char *name,
578                                              double *obj, Error **errp)
579 {
580     QObjectInputVisitor *qiv = to_qiv(v);
581     const char *str = qobject_input_get_keyval(qiv, name, errp);
582     double val;
583 
584     if (!str) {
585         return false;
586     }
587 
588     if (qemu_strtod_finite(str, NULL, &val)) {
589         /* TODO report -ERANGE more nicely */
590         error_setg(errp, "Invalid parameter type for '%s', expected: number",
591                    full_name(qiv, name));
592         return false;
593     }
594 
595     *obj = val;
596     return true;
597 }
598 
599 static bool qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
600                                    Error **errp)
601 {
602     QObjectInputVisitor *qiv = to_qiv(v);
603     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
604 
605     *obj = NULL;
606     if (!qobj) {
607         return false;
608     }
609 
610     *obj = qobject_ref(qobj);
611     return true;
612 }
613 
614 static bool qobject_input_type_null(Visitor *v, const char *name,
615                                     QNull **obj, Error **errp)
616 {
617     QObjectInputVisitor *qiv = to_qiv(v);
618     QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
619 
620     *obj = NULL;
621     if (!qobj) {
622         return false;
623     }
624 
625     if (qobject_type(qobj) != QTYPE_QNULL) {
626         error_setg(errp, "Invalid parameter type for '%s', expected: null",
627                    full_name(qiv, name));
628         return false;
629     }
630     *obj = qnull();
631     return true;
632 }
633 
634 static bool qobject_input_type_size_keyval(Visitor *v, const char *name,
635                                            uint64_t *obj, Error **errp)
636 {
637     QObjectInputVisitor *qiv = to_qiv(v);
638     const char *str = qobject_input_get_keyval(qiv, name, errp);
639 
640     if (!str) {
641         return false;
642     }
643 
644     if (qemu_strtosz(str, NULL, obj) < 0) {
645         /* TODO report -ERANGE more nicely */
646         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
647                    full_name(qiv, name), "size");
648         return false;
649     }
650     return true;
651 }
652 
653 static void qobject_input_optional(Visitor *v, const char *name, bool *present)
654 {
655     QObjectInputVisitor *qiv = to_qiv(v);
656     QObject *qobj = qobject_input_try_get_object(qiv, name, false);
657 
658     if (!qobj) {
659         *present = false;
660         return;
661     }
662 
663     *present = true;
664 }
665 
666 static bool qobject_input_policy_reject(Visitor *v, const char *name,
667                                         unsigned special_features,
668                                         Error **errp)
669 {
670     return !compat_policy_input_ok(special_features, &v->compat_policy,
671                                    ERROR_CLASS_GENERIC_ERROR,
672                                    "parameter", name, errp);
673 }
674 
675 static void qobject_input_free(Visitor *v)
676 {
677     QObjectInputVisitor *qiv = to_qiv(v);
678 
679     while (!QSLIST_EMPTY(&qiv->stack)) {
680         StackObject *tos = QSLIST_FIRST(&qiv->stack);
681 
682         QSLIST_REMOVE_HEAD(&qiv->stack, node);
683         qobject_input_stack_object_free(tos);
684     }
685 
686     qobject_unref(qiv->root);
687     if (qiv->errname) {
688         g_string_free(qiv->errname, TRUE);
689     }
690     g_free(qiv);
691 }
692 
693 static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj)
694 {
695     QObjectInputVisitor *v = g_malloc0(sizeof(*v));
696 
697     assert(obj);
698 
699     v->visitor.type = VISITOR_INPUT;
700     v->visitor.start_struct = qobject_input_start_struct;
701     v->visitor.check_struct = qobject_input_check_struct;
702     v->visitor.end_struct = qobject_input_end_struct;
703     v->visitor.start_list = qobject_input_start_list;
704     v->visitor.next_list = qobject_input_next_list;
705     v->visitor.check_list = qobject_input_check_list;
706     v->visitor.end_list = qobject_input_end_list;
707     v->visitor.start_alternate = qobject_input_start_alternate;
708     v->visitor.optional = qobject_input_optional;
709     v->visitor.policy_reject = qobject_input_policy_reject;
710     v->visitor.free = qobject_input_free;
711 
712     v->root = qobject_ref(obj);
713 
714     return v;
715 }
716 
717 Visitor *qobject_input_visitor_new(QObject *obj)
718 {
719     QObjectInputVisitor *v = qobject_input_visitor_base_new(obj);
720 
721     v->visitor.type_int64 = qobject_input_type_int64;
722     v->visitor.type_uint64 = qobject_input_type_uint64;
723     v->visitor.type_bool = qobject_input_type_bool;
724     v->visitor.type_str = qobject_input_type_str;
725     v->visitor.type_number = qobject_input_type_number;
726     v->visitor.type_any = qobject_input_type_any;
727     v->visitor.type_null = qobject_input_type_null;
728 
729     return &v->visitor;
730 }
731 
732 Visitor *qobject_input_visitor_new_keyval(QObject *obj)
733 {
734     QObjectInputVisitor *v = qobject_input_visitor_base_new(obj);
735 
736     v->visitor.type_int64 = qobject_input_type_int64_keyval;
737     v->visitor.type_uint64 = qobject_input_type_uint64_keyval;
738     v->visitor.type_bool = qobject_input_type_bool_keyval;
739     v->visitor.type_str = qobject_input_type_str_keyval;
740     v->visitor.type_number = qobject_input_type_number_keyval;
741     v->visitor.type_any = qobject_input_type_any;
742     v->visitor.type_null = qobject_input_type_null;
743     v->visitor.type_size = qobject_input_type_size_keyval;
744     v->keyval = true;
745 
746     return &v->visitor;
747 }
748 
749 Visitor *qobject_input_visitor_new_str(const char *str,
750                                        const char *implied_key,
751                                        Error **errp)
752 {
753     bool is_json = str[0] == '{';
754     QObject *obj;
755     QDict *args;
756     Visitor *v;
757 
758     if (is_json) {
759         obj = qobject_from_json(str, errp);
760         if (!obj) {
761             return NULL;
762         }
763         args = qobject_to(QDict, obj);
764         assert(args);
765         v = qobject_input_visitor_new(QOBJECT(args));
766     } else {
767         args = keyval_parse(str, implied_key, NULL, errp);
768         if (!args) {
769             return NULL;
770         }
771         v = qobject_input_visitor_new_keyval(QOBJECT(args));
772     }
773     qobject_unref(args);
774 
775     return v;
776 }
777