diff mbox

[v9,16/17] qapi: Tweak QmpInputVisitor to optionally do string conversion

Message ID 1468468228-27827-17-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake July 14, 2016, 3:50 a.m. UTC
Currently the QmpInputVisitor assumes that all scalar
values are directly represented as their final types.
ie it assumes an 'int' is using QInt, and a 'bool' is
using QBool.

This adds an alternative mode where a QString can also
be parsed in place of the native type, by adding a parameter
and updating all callers.  For now, only the testsuite sets
the new autocast parameter.

This makes it possible to use QmpInputVisitor with a QDict
produced from QemuOpts, where everything is in string format.
It also makes it possible for the next patch to support
back-compat in legacy commands like 'netdev_add' that no
longer use QemuOpts, but must parse the same set of QMP
constructs that QemuOpts would historically allow.

Based heavily on a patch by Daniel P. Berrange <berrange@redhat.com>:
Message-Id: <1467724312-9378-4-git-send-email-berrange@redhat.com>

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v9: new patch (but heavily draws from 3/7 in Dan's v7 series,
Provide a QOM-based authorization API)
---
 scripts/qapi-commands.py           |   2 +-
 include/qapi/qmp-input-visitor.h   |  24 ++++--
 qapi/qmp-input-visitor.c           | 100 ++++++++++++++++++++++---
 qmp.c                              |   2 +-
 qom/qom-qobject.c                  |   2 +-
 tests/check-qnull.c                |   2 +-
 tests/test-qmp-commands.c          |   2 +-
 tests/test-qmp-input-strict.c      |   2 +-
 tests/test-qmp-input-visitor.c     | 150 ++++++++++++++++++++++++++++++++++++-
 tests/test-visitor-serialization.c |   2 +-
 docs/qapi-code-gen.txt             |   2 +-
 11 files changed, 264 insertions(+), 26 deletions(-)

Comments

Daniel P. Berrangé July 14, 2016, 8:04 a.m. UTC | #1
On Wed, Jul 13, 2016 at 09:50:27PM -0600, Eric Blake wrote:
> Currently the QmpInputVisitor assumes that all scalar
> values are directly represented as their final types.
> ie it assumes an 'int' is using QInt, and a 'bool' is
> using QBool.
> 
> This adds an alternative mode where a QString can also
> be parsed in place of the native type, by adding a parameter
> and updating all callers.  For now, only the testsuite sets
> the new autocast parameter.
> 
> This makes it possible to use QmpInputVisitor with a QDict
> produced from QemuOpts, where everything is in string format.
> It also makes it possible for the next patch to support
> back-compat in legacy commands like 'netdev_add' that no
> longer use QemuOpts, but must parse the same set of QMP
> constructs that QemuOpts would historically allow.

I'm not really a huge fan of the approach there. IMHO the
input visitor should strictly operate in "all native types"
or "all string types" mode. The way you've done it here
means that users can actually mix & match strings vs native
types for each individual parameter :-(

IMHO, I'd suggest you try to parse netdev_add args with
it in "all native types" mode, if that fails, then re-parse
in "all string types" mode.

For that you'd not have to modify my patch at all.

Regards,
Daniel
Eric Blake July 14, 2016, 12:43 p.m. UTC | #2
On 07/14/2016 02:04 AM, Daniel P. Berrange wrote:
> On Wed, Jul 13, 2016 at 09:50:27PM -0600, Eric Blake wrote:
>> Currently the QmpInputVisitor assumes that all scalar
>> values are directly represented as their final types.
>> ie it assumes an 'int' is using QInt, and a 'bool' is
>> using QBool.
>>
>> This adds an alternative mode where a QString can also
>> be parsed in place of the native type, by adding a parameter
>> and updating all callers.  For now, only the testsuite sets
>> the new autocast parameter.
>>
>> This makes it possible to use QmpInputVisitor with a QDict
>> produced from QemuOpts, where everything is in string format.
>> It also makes it possible for the next patch to support
>> back-compat in legacy commands like 'netdev_add' that no
>> longer use QemuOpts, but must parse the same set of QMP
>> constructs that QemuOpts would historically allow.
> 
> I'm not really a huge fan of the approach there. IMHO the
> input visitor should strictly operate in "all native types"
> or "all string types" mode. The way you've done it here
> means that users can actually mix & match strings vs native
> types for each individual parameter :-(
> 
> IMHO, I'd suggest you try to parse netdev_add args with
> it in "all native types" mode, if that fails, then re-parse
> in "all string types" mode.

Sadly, this idea won't work. Without this series, the existing QMP
command 'netdev_add' takes mixed integers and strings in a single call,
by virtue of converting QObject into QemuOpts and back into QObject.
Forcing the parser to allow only integers or only strings would reject
current uses of netdev_add, and we don't yet have a good idea whether
any existing clients were depending on that behavior.

Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
is by setting a single flag which controls which visitor constructor is
used.  Your idea of parsing once with integer-only, then falling back to
parse a second time with string-only, would complicate the generator to
have to create two different visitors, rather than passing a single
boolean parameter through to the single visitor constructor.

> 
> For that you'd not have to modify my patch at all.
> 

Also not quite true - your patch no longer applies to master, so it
needed rebasing anyway.
Daniel P. Berrangé July 14, 2016, 1:03 p.m. UTC | #3
On Thu, Jul 14, 2016 at 06:43:16AM -0600, Eric Blake wrote:
> On 07/14/2016 02:04 AM, Daniel P. Berrange wrote:
> > On Wed, Jul 13, 2016 at 09:50:27PM -0600, Eric Blake wrote:
> >> Currently the QmpInputVisitor assumes that all scalar
> >> values are directly represented as their final types.
> >> ie it assumes an 'int' is using QInt, and a 'bool' is
> >> using QBool.
> >>
> >> This adds an alternative mode where a QString can also
> >> be parsed in place of the native type, by adding a parameter
> >> and updating all callers.  For now, only the testsuite sets
> >> the new autocast parameter.
> >>
> >> This makes it possible to use QmpInputVisitor with a QDict
> >> produced from QemuOpts, where everything is in string format.
> >> It also makes it possible for the next patch to support
> >> back-compat in legacy commands like 'netdev_add' that no
> >> longer use QemuOpts, but must parse the same set of QMP
> >> constructs that QemuOpts would historically allow.
> > 
> > I'm not really a huge fan of the approach there. IMHO the
> > input visitor should strictly operate in "all native types"
> > or "all string types" mode. The way you've done it here
> > means that users can actually mix & match strings vs native
> > types for each individual parameter :-(
> > 
> > IMHO, I'd suggest you try to parse netdev_add args with
> > it in "all native types" mode, if that fails, then re-parse
> > in "all string types" mode.
> 
> Sadly, this idea won't work. Without this series, the existing QMP
> command 'netdev_add' takes mixed integers and strings in a single call,
> by virtue of converting QObject into QemuOpts and back into QObject.

Ok, so lemme check I understand. The original QObject has properties
which are integers, but with existing code QMP allows them to be passed
as strings or ints, converts them to QemuOpts which makes them all
strings and converts them back to QObject leaving them all as strings.

You've now got rid of the QObject -> QemuOpts conversion, so we're
not string-ifying everything, and so have to cope with arbitrary
mix directly.

> Forcing the parser to allow only integers or only strings would reject
> current uses of netdev_add, and we don't yet have a good idea whether
> any existing clients were depending on that behavior.
> 
> Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
> is by setting a single flag which controls which visitor constructor is
> used.  Your idea of parsing once with integer-only, then falling back to
> parse a second time with string-only, would complicate the generator to
> have to create two different visitors, rather than passing a single
> boolean parameter through to the single visitor constructor.

So I don't think a simple boolean flag is desirable, as we have 3 distinct
scenarios:

 1. Strongly typed only
 2. String conversion only
 3. Strongly typed, with string conversion fallback

My current patch provides 1 + 2, your alternative patch provides 1 + 3.
If we use option 3, in cases which only actually need option 2, then we
are potentially introducing more scenarios where arbitrarily mixed
types are accepted.  IOW, I think we must implement 1, 2 and 3 and avoid
using 3 except where absolutely needed.

Regards,
Daniel
Eric Blake July 14, 2016, 2:04 p.m. UTC | #4
On 07/14/2016 07:03 AM, Daniel P. Berrange wrote:

>>> I'm not really a huge fan of the approach there. IMHO the
>>> input visitor should strictly operate in "all native types"
>>> or "all string types" mode. The way you've done it here
>>> means that users can actually mix & match strings vs native
>>> types for each individual parameter :-(
>>>
>>> IMHO, I'd suggest you try to parse netdev_add args with
>>> it in "all native types" mode, if that fails, then re-parse
>>> in "all string types" mode.
>>
>> Sadly, this idea won't work. Without this series, the existing QMP
>> command 'netdev_add' takes mixed integers and strings in a single call,
>> by virtue of converting QObject into QemuOpts and back into QObject.
> 
> Ok, so lemme check I understand. The original QObject has properties
> which are integers, but with existing code QMP allows them to be passed
> as strings or ints, converts them to QemuOpts which makes them all
> strings and converts them back to QObject leaving them all as strings.

Yes, the existing code (using 'gen':false) just passes an entire QObject
to hand-written code; the hand-written code converted everything to
QemuOpts (which flattens both integers and strings into options), then
expanded QemuOpts back into QObject (strings-only).

> 
> You've now got rid of the QObject -> QemuOpts conversion, so we're
> not string-ifying everything, and so have to cope with arbitrary
> mix directly.

Yes.

> 
>> Forcing the parser to allow only integers or only strings would reject
>> current uses of netdev_add, and we don't yet have a good idea whether
>> any existing clients were depending on that behavior.
>>
>> Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
>> is by setting a single flag which controls which visitor constructor is
>> used.  Your idea of parsing once with integer-only, then falling back to
>> parse a second time with string-only, would complicate the generator to
>> have to create two different visitors, rather than passing a single
>> boolean parameter through to the single visitor constructor.
> 
> So I don't think a simple boolean flag is desirable, as we have 3 distinct
> scenarios:
> 
>  1. Strongly typed only
>  2. String conversion only
>  3. Strongly typed, with string conversion fallback
> 
> My current patch provides 1 + 2, your alternative patch provides 1 + 3.
> If we use option 3, in cases which only actually need option 2, then we
> are potentially introducing more scenarios where arbitrarily mixed
> types are accepted.  IOW, I think we must implement 1, 2 and 3 and avoid
> using 3 except where absolutely needed.

3 is a superset of 2, and your command-line conversion is the only case
where we can achieve 2.  That is, code dealing with QMP can only choose
between 1 and 3, based on whether the QAPI .json file used
'autocast':true for back-compat reasons (the only candidates are
'netdev_add' and 'device_add').  And code dealing with command line
parsing can only choose 2 (QemuOpts is string-only), but parsing
string-only via 2 is no different than the result achieved from parsing
strongly-typed with string fallback via 3.

I still don't buy the fact that we need a string-only parser at the
moment, but it would not be hard to change the 'bool autocast' into a
tri-state enum, and then make the implementation specifically honor 1,
2, or 3 based on the enum value.
Daniel P. Berrangé July 14, 2016, 2:16 p.m. UTC | #5
On Thu, Jul 14, 2016 at 08:04:29AM -0600, Eric Blake wrote:
> On 07/14/2016 07:03 AM, Daniel P. Berrange wrote:
> 
> >>> I'm not really a huge fan of the approach there. IMHO the
> >>> input visitor should strictly operate in "all native types"
> >>> or "all string types" mode. The way you've done it here
> >>> means that users can actually mix & match strings vs native
> >>> types for each individual parameter :-(
> >>>
> >>> IMHO, I'd suggest you try to parse netdev_add args with
> >>> it in "all native types" mode, if that fails, then re-parse
> >>> in "all string types" mode.
> >>
> >> Sadly, this idea won't work. Without this series, the existing QMP
> >> command 'netdev_add' takes mixed integers and strings in a single call,
> >> by virtue of converting QObject into QemuOpts and back into QObject.
> > 
> > Ok, so lemme check I understand. The original QObject has properties
> > which are integers, but with existing code QMP allows them to be passed
> > as strings or ints, converts them to QemuOpts which makes them all
> > strings and converts them back to QObject leaving them all as strings.
> 
> Yes, the existing code (using 'gen':false) just passes an entire QObject
> to hand-written code; the hand-written code converted everything to
> QemuOpts (which flattens both integers and strings into options), then
> expanded QemuOpts back into QObject (strings-only).
> 
> > 
> > You've now got rid of the QObject -> QemuOpts conversion, so we're
> > not string-ifying everything, and so have to cope with arbitrary
> > mix directly.
> 
> Yes.
> 
> > 
> >> Forcing the parser to allow only integers or only strings would reject
> >> current uses of netdev_add, and we don't yet have a good idea whether
> >> any existing clients were depending on that behavior.
> >>
> >> Also, see patch 17/17 - the easiest way to implement a relaxed QMP parse
> >> is by setting a single flag which controls which visitor constructor is
> >> used.  Your idea of parsing once with integer-only, then falling back to
> >> parse a second time with string-only, would complicate the generator to
> >> have to create two different visitors, rather than passing a single
> >> boolean parameter through to the single visitor constructor.
> > 
> > So I don't think a simple boolean flag is desirable, as we have 3 distinct
> > scenarios:
> > 
> >  1. Strongly typed only
> >  2. String conversion only
> >  3. Strongly typed, with string conversion fallback
> > 
> > My current patch provides 1 + 2, your alternative patch provides 1 + 3.
> > If we use option 3, in cases which only actually need option 2, then we
> > are potentially introducing more scenarios where arbitrarily mixed
> > types are accepted.  IOW, I think we must implement 1, 2 and 3 and avoid
> > using 3 except where absolutely needed.
> 
> 3 is a superset of 2, and your command-line conversion is the only case
> where we can achieve 2.  That is, code dealing with QMP can only choose
> between 1 and 3, based on whether the QAPI .json file used
> 'autocast':true for back-compat reasons (the only candidates are
> 'netdev_add' and 'device_add').  And code dealing with command line
> parsing can only choose 2 (QemuOpts is string-only), but parsing
> string-only via 2 is no different than the result achieved from parsing
> strongly-typed with string fallback via 3.
> 
> I still don't buy the fact that we need a string-only parser at the
> moment, but it would not be hard to change the 'bool autocast' into a
> tri-state enum, and then make the implementation specifically honor 1,
> 2, or 3 based on the enum value.

FYI, I just copied you on a patch that enables us to easily support
all 3 options.


Regards,
Daniel
Eric Blake July 14, 2016, 2:25 p.m. UTC | #6
On 07/14/2016 08:16 AM, Daniel P. Berrange wrote:
> On Thu, Jul 14, 2016 at 08:04:29AM -0600, Eric Blake wrote:
>> On 07/14/2016 07:03 AM, Daniel P. Berrange wrote:
>>

>> 3 is a superset of 2, and your command-line conversion is the only case
>> where we can achieve 2.  That is, code dealing with QMP can only choose
>> between 1 and 3, based on whether the QAPI .json file used
>> 'autocast':true for back-compat reasons (the only candidates are
>> 'netdev_add' and 'device_add').  And code dealing with command line
>> parsing can only choose 2 (QemuOpts is string-only), but parsing
>> string-only via 2 is no different than the result achieved from parsing
>> strongly-typed with string fallback via 3.
>>
>> I still don't buy the fact that we need a string-only parser at the
>> moment, but it would not be hard to change the 'bool autocast' into a
>> tri-state enum, and then make the implementation specifically honor 1,
>> 2, or 3 based on the enum value.
> 
> FYI, I just copied you on a patch that enables us to easily support
> all 3 options.

Yours achieves that by having three separate constructors, instead of
one constructor with a tri-state enum parameter.  Either approach works;
Markus, do you have any opinions on which looks nicer?
diff mbox

Patch

diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index a06a2c4..f526c13 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -117,7 +117,7 @@  def gen_marshal(name, arg_type, boxed, ret_type):
     Visitor *v;
     %(c_name)s arg = {0};

-    v = qmp_input_visitor_new(QOBJECT(args), true);
+    v = qmp_input_visitor_new(QOBJECT(args), true, false);
     visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h
index f3ff5f3..275a167 100644
--- a/include/qapi/qmp-input-visitor.h
+++ b/include/qapi/qmp-input-visitor.h
@@ -19,12 +19,26 @@ 

 typedef struct QmpInputVisitor QmpInputVisitor;

-/*
- * Return a new input visitor that converts QMP to QAPI.
+/**
+ * qmp_input_visitor_new:
+ * @obj: the input object to visit
+ * @strict: whether to require that all input keys are consumed
+ * @autocast: whether to allow strings in place of other scalars
  *
- * Set @strict to reject a parse that doesn't consume all keys of a
- * dictionary; otherwise excess input is ignored.
+ * Create a new input visitor that converts QMP to QAPI.
+ *
+ * If @strict is set to true, then an error will be reported if any
+ * dict keys are not consumed during visitation.
+ *
+ * When @autocast is false, any scalar values in the @obj input data
+ * structure should be in the required type already. ie if visiting a
+ * bool, the value should already be a QBool instance.  Otherwise, a
+ * string value that can be parsed as the scalar is acceptable;
+ * however, it is not possible to parse QAPI alternates in autocast
+ * mode.
+ *
+ * Returns: a new input visitor
  */
-Visitor *qmp_input_visitor_new(QObject *obj, bool strict);
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict, bool autocast);

 #endif
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 64dd392..7a1b93a 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -20,6 +20,7 @@ 
 #include "qemu-common.h"
 #include "qapi/qmp/types.h"
 #include "qapi/qmp/qerror.h"
+#include "qemu/cutils.h"

 #define QIV_STACK_SIZE 1024

@@ -47,6 +48,9 @@  struct QmpInputVisitor

     /* True to reject parse in visit_end_struct() if unvisited keys remain. */
     bool strict;
+
+    /* True to allow strings in place of native scalars. */
+    bool autocast;
 };

 static QmpInputVisitor *to_qiv(Visitor *v)
@@ -234,6 +238,8 @@  static void qmp_input_start_alternate(Visitor *v, const char *name,
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, false);

+    assert(!qiv->autocast);
+
     if (!qobj) {
         *obj = NULL;
         error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null");
@@ -250,11 +256,21 @@  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));
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QInt *qint = qobject_to_qint(qobj);

     if (!qint) {
-        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
-                   "integer");
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            uint64_t ret = *obj;
+
+            parse_option_number(name, qstr->string, &ret, errp);
+            *obj = ret;
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "integer");
+        }
         return;
     }

@@ -266,11 +282,18 @@  static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 {
     /* 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));
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QInt *qint = qobject_to_qint(qobj);

     if (!qint) {
-        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
-                   "integer");
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            parse_option_number(name, qstr->string, obj, errp);
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "integer");
+        }
         return;
     }

@@ -281,11 +304,18 @@  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));
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QBool *qbool = qobject_to_qbool(qobj);

     if (!qbool) {
-        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
-                   "boolean");
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            parse_option_bool(name, qstr->string, obj, errp);
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "boolean");
+        }
         return;
     }

@@ -315,6 +345,7 @@  static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
     QObject *qobj = qmp_input_get_object(qiv, name, true);
     QInt *qint;
     QFloat *qfloat;
+    QString *qstr;

     qint = qobject_to_qint(qobj);
     if (qint) {
@@ -328,6 +359,19 @@  static void qmp_input_type_number(Visitor *v, const char *name, double *obj,
         return;
     }

+    qstr = qobject_to_qstring(qobj);
+    if (qiv->autocast && qstr) {
+        char *endp;
+        double value;
+
+        errno = 0;
+        value = strtod(qstr->string, &endp);
+        if (errno == 0 && endp != qstr->string && *endp == '\0') {
+            *obj = value;
+            return;
+        }
+    }
+
     error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                "number");
 }
@@ -353,6 +397,40 @@  static void qmp_input_type_null(Visitor *v, const char *name, Error **errp)
     }
 }

+static void qmp_input_type_size(Visitor *v, const char *name, uint64_t *obj,
+                                Error **errp)
+{
+    /* FIXME: qobject_to_qint mishandles values over INT64_MAX */
+    QmpInputVisitor *qiv = to_qiv(v);
+    QObject *qobj = qmp_input_get_object(qiv, name, true);
+    QInt *qint = qobject_to_qint(qobj);
+
+    if (!qint) {
+        QString *qstr = qobject_to_qstring(qobj);
+
+        if (qiv->autocast && qstr) {
+            int64_t val;
+            char *endptr;
+
+            val = qemu_strtosz_suffix(qstr->string, &endptr,
+                                      QEMU_STRTOSZ_DEFSUFFIX_B);
+            if (val < 0 || *endptr) {
+                error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
+                           "a size value representible as a non-negative"
+                           " int64");
+            } else {
+                *obj = val;
+            }
+        } else {
+            error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
+                       "integer");
+        }
+        return;
+    }
+
+    *obj = qint_get_int(qint);
+}
+
 static void qmp_input_optional(Visitor *v, const char *name, bool *present)
 {
     QmpInputVisitor *qiv = to_qiv(v);
@@ -380,7 +458,7 @@  static void qmp_input_free(Visitor *v)
     g_free(qiv);
 }

-Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
+Visitor *qmp_input_visitor_new(QObject *obj, bool strict, bool autocast)
 {
     QmpInputVisitor *v;

@@ -401,9 +479,11 @@  Visitor *qmp_input_visitor_new(QObject *obj, bool strict)
     v->visitor.type_number = qmp_input_type_number;
     v->visitor.type_any = qmp_input_type_any;
     v->visitor.type_null = qmp_input_type_null;
+    v->visitor.type_size = qmp_input_type_size;
     v->visitor.optional = qmp_input_optional;
     v->visitor.free = qmp_input_free;
     v->strict = strict;
+    v->autocast = autocast;

     v->root = obj;
     qobject_incref(obj);
diff --git a/qmp.c b/qmp.c
index b6d531e..ff93890 100644
--- a/qmp.c
+++ b/qmp.c
@@ -666,7 +666,7 @@  void qmp_object_add(const char *type, const char *id,
         }
     }

-    v = qmp_input_visitor_new(props, true);
+    v = qmp_input_visitor_new(props, true, false);
     obj = user_creatable_add_type(type, id, pdict, v, errp);
     visit_free(v);
     if (obj) {
diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c
index c225abc..974c7ea 100644
--- a/qom/qom-qobject.c
+++ b/qom/qom-qobject.c
@@ -23,7 +23,7 @@  void object_property_set_qobject(Object *obj, QObject *value,
 {
     Visitor *v;
     /* TODO: Should we reject, rather than ignore, excess input? */
-    v = qmp_input_visitor_new(value, false);
+    v = qmp_input_visitor_new(value, false, false);
     object_property_set(obj, v, name, errp);
     visit_free(v);
 }
diff --git a/tests/check-qnull.c b/tests/check-qnull.c
index dc906b1..483ed6d 100644
--- a/tests/check-qnull.c
+++ b/tests/check-qnull.c
@@ -47,7 +47,7 @@  static void qnull_visit_test(void)

     g_assert(qnull_.refcnt == 1);
     obj = qnull();
-    v = qmp_input_visitor_new(obj, true);
+    v = qmp_input_visitor_new(obj, true, false);
     qobject_decref(obj);
     visit_type_null(v, NULL, &error_abort);
     visit_free(v);
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 5af1a46..974841c 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -229,7 +229,7 @@  static void test_dealloc_partial(void)
         ud2_dict = qdict_new();
         qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));

-        v = qmp_input_visitor_new(QOBJECT(ud2_dict), true);
+        v = qmp_input_visitor_new(QOBJECT(ud2_dict), true, false);
         visit_type_UserDefTwo(v, NULL, &ud2, &err);
         visit_free(v);
         QDECREF(ud2_dict);
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 814550a..93ee137 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -53,7 +53,7 @@  static Visitor *validate_test_init_internal(TestInputVisitorData *data,
     data->obj = qobject_from_jsonv(json_string, ap);
     g_assert(data->obj);

-    data->qiv = qmp_input_visitor_new(data->obj, true);
+    data->qiv = qmp_input_visitor_new(data->obj, true, false);
     g_assert(data->qiv);
     return data->qiv;
 }
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index f583dce..29c4562 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -41,6 +41,7 @@  static void visitor_input_teardown(TestInputVisitorData *data,
    function so that the JSON string used by the tests are kept in the test
    functions (and not in main()). */
 static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
+                                                 bool strict, bool autocast,
                                                  const char *json_string,
                                                  va_list *ap)
 {
@@ -49,11 +50,26 @@  static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
     data->obj = qobject_from_jsonv(json_string, ap);
     g_assert(data->obj);

-    data->qiv = qmp_input_visitor_new(data->obj, false);
+    data->qiv = qmp_input_visitor_new(data->obj, strict, autocast);
     g_assert(data->qiv);
     return data->qiv;
 }

+static GCC_FMT_ATTR(4, 5)
+Visitor *visitor_input_test_init_full(TestInputVisitorData *data,
+                                      bool strict, bool autocast,
+                                      const char *json_string, ...)
+{
+    Visitor *v;
+    va_list ap;
+
+    va_start(ap, json_string);
+    v = visitor_input_test_init_internal(data, strict, autocast,
+                                         json_string, &ap);
+    va_end(ap);
+    return v;
+}
+
 static GCC_FMT_ATTR(2, 3)
 Visitor *visitor_input_test_init(TestInputVisitorData *data,
                                  const char *json_string, ...)
@@ -62,7 +78,8 @@  Visitor *visitor_input_test_init(TestInputVisitorData *data,
     va_list ap;

     va_start(ap, json_string);
-    v = visitor_input_test_init_internal(data, json_string, &ap);
+    v = visitor_input_test_init_internal(data, false, false,
+                                         json_string, &ap);
     va_end(ap);
     return v;
 }
@@ -77,7 +94,8 @@  Visitor *visitor_input_test_init(TestInputVisitorData *data,
 static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data,
                                             const char *json_string)
 {
-    return visitor_input_test_init_internal(data, json_string, NULL);
+    return visitor_input_test_init_internal(data, false, false,
+                                            json_string, NULL);
 }

 static void test_visitor_in_int(TestInputVisitorData *data,
@@ -109,6 +127,33 @@  static void test_visitor_in_int_overflow(TestInputVisitorData *data,
     error_free_or_abort(&err);
 }

+static void test_visitor_in_int_autocast(TestInputVisitorData *data,
+                                         const void *unused)
+{
+    int64_t res = 0, value = -42;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true,
+                                     "\"-42\"");
+
+    visit_type_int(v, NULL, &res, &error_abort);
+    g_assert_cmpint(res, ==, value);
+}
+
+static void test_visitor_in_int_noautocast(TestInputVisitorData *data,
+                                           const void *unused)
+{
+    int64_t res = 0;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"-42\"");
+
+    visit_type_int(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
 static void test_visitor_in_bool(TestInputVisitorData *data,
                                  const void *unused)
 {
@@ -121,6 +166,32 @@  static void test_visitor_in_bool(TestInputVisitorData *data,
     g_assert_cmpint(res, ==, true);
 }

+static void test_visitor_in_bool_autocast(TestInputVisitorData *data,
+                                          const void *unused)
+{
+    bool res = false;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true, "\"yes\"");
+
+    visit_type_bool(v, NULL, &res, &error_abort);
+    g_assert_cmpint(res, ==, true);
+}
+
+static void test_visitor_in_bool_noautocast(TestInputVisitorData *data,
+                                          const void *unused)
+{
+    bool res = false;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"true\"");
+
+    visit_type_bool(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
 static void test_visitor_in_number(TestInputVisitorData *data,
                                    const void *unused)
 {
@@ -133,6 +204,63 @@  static void test_visitor_in_number(TestInputVisitorData *data,
     g_assert_cmpfloat(res, ==, value);
 }

+static void test_visitor_in_number_autocast(TestInputVisitorData *data,
+                                            const void *unused)
+{
+    double res = 0, value = 3.14;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true, "\"3.14\"");
+
+    visit_type_number(v, NULL, &res, &error_abort);
+    g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_number_noautocast(TestInputVisitorData *data,
+                                              const void *unused)
+{
+    double res = 0;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"3.14\"");
+
+    visit_type_number(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
+static void test_visitor_in_size_autocast(TestInputVisitorData *data,
+                                            const void *unused)
+{
+    uint64_t res, value = 500 * 1024 * 1024;
+    Visitor *v;
+
+    v = visitor_input_test_init_full(data, false, true, "\"500M\"");
+
+    visit_type_size(v, NULL, &res, &error_abort);
+    g_assert_cmpfloat(res, ==, value);
+
+    v = visitor_input_test_init_full(data, false, true, "524288000");
+
+    visit_type_size(v, NULL, &res, &error_abort);
+    g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_size_noautocast(TestInputVisitorData *data,
+                                            const void *unused)
+{
+    uint64_t res = 0;
+    Visitor *v;
+    Error *err = NULL;
+
+    v = visitor_input_test_init(data, "\"500M\"");
+
+    visit_type_size(v, NULL, &res, &err);
+    g_assert(err != NULL);
+    error_free(err);
+}
+
 static void test_visitor_in_string(TestInputVisitorData *data,
                                    const void *unused)
 {
@@ -841,10 +969,26 @@  int main(int argc, char **argv)
                            &in_visitor_data, test_visitor_in_int);
     input_visitor_test_add("/visitor/input/int_overflow",
                            &in_visitor_data, test_visitor_in_int_overflow);
+    input_visitor_test_add("/visitor/input/int_autocast",
+                           &in_visitor_data, test_visitor_in_int_autocast);
+    input_visitor_test_add("/visitor/input/int_noautocast",
+                           &in_visitor_data, test_visitor_in_int_noautocast);
     input_visitor_test_add("/visitor/input/bool",
                            &in_visitor_data, test_visitor_in_bool);
+    input_visitor_test_add("/visitor/input/bool_autocast",
+                           &in_visitor_data, test_visitor_in_bool_autocast);
+    input_visitor_test_add("/visitor/input/bool_noautocast",
+                           &in_visitor_data, test_visitor_in_bool_noautocast);
     input_visitor_test_add("/visitor/input/number",
                            &in_visitor_data, test_visitor_in_number);
+    input_visitor_test_add("/visitor/input/number_autocast",
+                           &in_visitor_data, test_visitor_in_number_autocast);
+    input_visitor_test_add("/visitor/input/number_noautocast",
+                           &in_visitor_data, test_visitor_in_number_noautocast);
+    input_visitor_test_add("/visitor/input/size_autocast",
+                           &in_visitor_data, test_visitor_in_size_autocast);
+    input_visitor_test_add("/visitor/input/size_noautocast",
+                           &in_visitor_data, test_visitor_in_size_noautocast);
     input_visitor_test_add("/visitor/input/string",
                            &in_visitor_data, test_visitor_in_string);
     input_visitor_test_add("/visitor/input/enum",
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index dba4670..ddd5837 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1040,7 +1040,7 @@  static void qmp_deserialize(void **native_out, void *datap,
     obj = qobject_from_json(qstring_get_str(output_json));

     QDECREF(output_json);
-    d->qiv = qmp_input_visitor_new(obj, true);
+    d->qiv = qmp_input_visitor_new(obj, true, false);
     qobject_decref(obj_orig);
     qobject_decref(obj);
     visit(d->qiv, native_out, errp);
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index de298dc..961015c 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -1024,7 +1024,7 @@  Example:
         Visitor *v;
         UserDefOneList *arg1 = NULL;

-        v = qmp_input_visitor_new(QOBJECT(args), true);
+        v = qmp_input_visitor_new(QOBJECT(args), true, false);
         visit_start_struct(v, NULL, NULL, 0, &err);
         if (err) {
             goto out;