QAPI patches for 2018-12-13

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJcE0VvAAoJEDhwtADrkYZTLCkP/RvRR9iTcoM98kcFNjqBZRQa
 rUbSNBavxwzutPiT40WcNhg7hc0Uaptve8oMkGcfyyTh9UyhdOe8WNPxTos96vYt
 GtUhNhknGlvP4A7Zjs6KIIhl084MtPkpuPERkXZL4lgNrIw8BrFoj5hkZ3UIvItf
 14oA1o6Zf9UxN1Yt12lZnG9N8t4ld5IKhkXh/FQ6OJNHz9GrhPq4A7vd4ipBRBjt
 PjvXVOYCEkiHRfJ3Qv5Thk2C1xzLRFusA5ff1rju324KGPoM8oZ+xGSUVqD0hhMe
 Kpzv4a6HV7SuM1fqJoZrF87VOhAO9bpxzIHUp83FhpKGDH4xqppDWYno/+9imPDA
 DAHUaOeaKpX6O4ttB96jRwTEOAbq3TzPqtYiyRaXhbtCc0dKi0HxHmIpwS4KNkHK
 Y3VuoTavarMfuLl2gDO+9PJhHxol8g0oYiaxXddW0svgnSM3xBTz/hGE2duStHTb
 DSWDVB/oVIOyR8eWSglUnc+OOJrxSkiaJelSU730Uc6kIk7hiY8PFQiwqebsI6uq
 IOABDG1/W0FkSRNl5QwXnGlD0eUzl1ySm2zvsgvJrC8ooAhzjjWdkcwtEdEYxlUj
 KqH+8ZFP+mOckrW9boqYPVqOL4GzNMnK23vEoidurhyShsmiCTyk+jckiJrl/IMy
 OlwA850MKVJ3W3+knR0I
 =bymN
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2018-12-13-v2' into staging

QAPI patches for 2018-12-13

# gpg: Signature made Fri 14 Dec 2018 05:53:51 GMT
# gpg:                using RSA key 3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2018-12-13-v2: (32 commits)
  qapi: add conditions to REPLICATION type/commands on the schema
  qapi: add more conditions to SPICE
  qapi: add condition to variants documentation
  qapi: add 'If:' condition to struct members documentation
  qapi: add 'If:' condition to enum values documentation
  qapi: Add #if conditions to generated code members
  qapi: add 'if' to alternate members
  qapi: add 'if' to union members
  qapi: Add 'if' to implicit struct members
  qapi: add a dictionary form for TYPE
  qapi-events: add 'if' condition to implicit event enum
  qapi: add 'if' to enum members
  qapi: add a dictionary form with 'name' key for enum members
  qapi: improve reporting of unknown or missing keys
  qapi: factor out checking for keys
  tests: print enum type members more like object type members
  qapi: change enum visitor and gen_enum* to take QAPISchemaMember
  qapi: Do not define enumeration value explicitly
  qapi: break long lines at 'data' member
  qapi: rename QAPISchemaEnumType.values to .members
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-12-15 21:19:06 +00:00
commit 81781be3c9
91 changed files with 1166 additions and 510 deletions

View File

@ -26,7 +26,7 @@ how the schemas, scripts, and resulting code are used.
== QMP/Guest agent schema ==
A QAPI schema file is designed to be loosely based on JSON
(http://www.ietf.org/rfc/rfc7159.txt) with changes for quoting style
(http://www.ietf.org/rfc/rfc8259.txt) with changes for quoting style
and the use of comments; a QAPI schema file is then parsed by a python
code generation program. A valid QAPI schema consists of a series of
top-level expressions, with no commas between them. Where
@ -752,6 +752,25 @@ gets its generated code guarded like this:
#endif /* defined(HAVE_BAR) */
#endif /* defined(CONFIG_FOO) */
Where a member can be defined with a single string value for its type,
it is also possible to supply a dictionary instead with both 'type'
and 'if' keys.
Example: a conditional 'bar' member
{ 'struct': 'IfStruct', 'data':
{ 'foo': 'int',
'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } }
An enum value can be replaced by a dictionary with a 'name' and a 'if'
key.
Example: a conditional 'bar' enum member.
{ 'enum': 'IfEnum', 'data':
[ 'foo',
{ 'name' : 'bar', 'if': 'defined(IFCOND)' } ] }
Please note that you are responsible to ensure that the C code will
compile with an arbitrary combination of conditions, since the
generators are unable to check it at this point.

View File

@ -32,7 +32,7 @@ following format:
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined
by the JSON standard:
http://www.ietf.org/rfc/rfc7159.txt
http://www.ietf.org/rfc/rfc8259.txt
The server expects its input to be encoded in UTF-8, and sends its
output encoded in ASCII.

View File

@ -19,8 +19,8 @@ typedef struct StringInputVisitor StringInputVisitor;
/*
* The string input visitor does not implement support for visiting
* QAPI structs, alternates, null, or arbitrary QTypes. It also
* requires a non-null list argument to visit_start_list().
* QAPI structs, alternates, null, or arbitrary QTypes. Only flat lists
* of integers (except type "size") are supported.
*/
Visitor *string_input_visitor_new(const char *str);

View File

@ -146,14 +146,16 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base,
int64_t *result);
int qemu_strtou64(const char *nptr, const char **endptr, int base,
uint64_t *result);
int qemu_strtod(const char *nptr, const char **endptr, double *result);
int qemu_strtod_finite(const char *nptr, const char **endptr, double *result);
int parse_uint(const char *s, unsigned long long *value, char **endptr,
int base);
int parse_uint_full(const char *s, unsigned long long *value, int base);
int qemu_strtosz(const char *nptr, char **end, uint64_t *result);
int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result);
int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result);
int qemu_strtosz(const char *nptr, const char **end, uint64_t *result);
int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result);
int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result);
/* used to print char* safely */
#define STR_OR_NULL(str) ((str) ? (str) : "null")

View File

@ -24,7 +24,9 @@
#include "trace.h"
#include "qemu/error-report.h"
#include "migration/failover.h"
#ifdef CONFIG_REPLICATION
#include "replication.h"
#endif
#include "net/colo-compare.h"
#include "net/colo.h"
#include "block/block.h"
@ -201,11 +203,11 @@ void colo_do_failover(MigrationState *s)
}
}
#ifdef CONFIG_REPLICATION
void qmp_xen_set_replication(bool enable, bool primary,
bool has_failover, bool failover,
Error **errp)
{
#ifdef CONFIG_REPLICATION
ReplicationMode mode = primary ?
REPLICATION_MODE_PRIMARY :
REPLICATION_MODE_SECONDARY;
@ -224,14 +226,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
}
replication_stop_all(failover, failover ? NULL : errp);
}
#else
abort();
#endif
}
ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
{
#ifdef CONFIG_REPLICATION
Error *err = NULL;
ReplicationStatus *s = g_new0(ReplicationStatus, 1);
@ -246,19 +244,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
error_free(err);
return s;
#else
abort();
#endif
}
void qmp_xen_colo_do_checkpoint(Error **errp)
{
#ifdef CONFIG_REPLICATION
replication_do_checkpoint_all(errp);
#else
abort();
#endif
}
#endif
COLOStatus *qmp_query_colo_status(Error **errp)
{

View File

@ -1147,11 +1147,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
*/
static void qmp_unregister_commands_hack(void)
{
#ifndef CONFIG_REPLICATION
qmp_unregister_command(&qmp_commands, "xen-set-replication");
qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
#endif
#ifndef TARGET_I386
qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
qmp_unregister_command(&qmp_commands, "query-sev");
@ -3232,7 +3227,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
{
int ret;
uint64_t val;
char *end;
const char *end;
while (qemu_isspace(*p)) {
p++;

View File

@ -1143,8 +1143,10 @@
# This command is now obsolete and will always return an error since 2.10
#
##
{ 'command': 'block_passwd', 'data': {'*device': 'str',
'*node-name': 'str', 'password': 'str'} }
{ 'command': 'block_passwd',
'data': { '*device': 'str',
'*node-name': 'str',
'password': 'str' } }
##
# @block_resize:
@ -1171,9 +1173,10 @@
# <- { "return": {} }
#
##
{ 'command': 'block_resize', 'data': { '*device': 'str',
'*node-name': 'str',
'size': 'int' }}
{ 'command': 'block_resize',
'data': { '*device': 'str',
'*node-name': 'str',
'size': 'int' } }
##
# @NewImageMode:
@ -2620,7 +2623,9 @@
'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
'qcow2', 'qed', 'quorum', 'raw', 'rbd',
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
'sheepdog',
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
##
@ -3377,7 +3382,8 @@
#
# Since: 2.9
##
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
'if': 'defined(CONFIG_REPLICATION)' }
##
# @BlockdevOptionsReplication:
@ -3395,7 +3401,8 @@
{ 'struct': 'BlockdevOptionsReplication',
'base': 'BlockdevOptionsGenericFormat',
'data': { 'mode': 'ReplicationMode',
'*top-id': 'str' } }
'*top-id': 'str' },
'if': 'defined(CONFIG_REPLICATION)' }
##
# @NFSTransport:
@ -3711,7 +3718,8 @@
'quorum': 'BlockdevOptionsQuorum',
'raw': 'BlockdevOptionsRaw',
'rbd': 'BlockdevOptionsRbd',
'replication':'BlockdevOptionsReplication',
'replication': { 'type': 'BlockdevOptionsReplication',
'if': 'defined(CONFIG_REPLICATION)' },
'sheepdog': 'BlockdevOptionsSheepdog',
'ssh': 'BlockdevOptionsSsh',
'throttle': 'BlockdevOptionsThrottle',

View File

@ -25,9 +25,10 @@
#
# Since: 0.14.0
##
{ 'struct': 'ChardevInfo', 'data': {'label': 'str',
'filename': 'str',
'frontend-open': 'bool'} }
{ 'struct': 'ChardevInfo',
'data': { 'label': 'str',
'filename': 'str',
'frontend-open': 'bool' } }
##
# @query-chardev:
@ -152,7 +153,8 @@
#
##
{ 'command': 'ringbuf-write',
'data': {'device': 'str', 'data': 'str',
'data': { 'device': 'str',
'data': 'str',
'*format': 'DataFormat'} }
##
@ -202,8 +204,9 @@
#
# Since: 2.6
##
{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str',
'*logappend': 'bool' } }
{ 'struct': 'ChardevCommon',
'data': { '*logfile': 'str',
'*logappend': 'bool' } }
##
# @ChardevFile:
@ -217,9 +220,10 @@
#
# Since: 1.4
##
{ 'struct': 'ChardevFile', 'data': { '*in' : 'str',
'out' : 'str',
'*append': 'bool' },
{ 'struct': 'ChardevFile',
'data': { '*in': 'str',
'out': 'str',
'*append': 'bool' },
'base': 'ChardevCommon' }
##
@ -232,7 +236,8 @@
#
# Since: 1.4
##
{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' },
{ 'struct': 'ChardevHostdev',
'data': { 'device': 'str' },
'base': 'ChardevCommon' }
##
@ -260,15 +265,16 @@
#
# Since: 1.4
##
{ 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddressLegacy',
'*tls-creds' : 'str',
'*server' : 'bool',
'*wait' : 'bool',
'*nodelay' : 'bool',
'*telnet' : 'bool',
'*tn3270' : 'bool',
'*websocket' : 'bool',
'*reconnect' : 'int' },
{ 'struct': 'ChardevSocket',
'data': { 'addr': 'SocketAddressLegacy',
'*tls-creds': 'str',
'*server': 'bool',
'*wait': 'bool',
'*nodelay': 'bool',
'*telnet': 'bool',
'*tn3270': 'bool',
'*websocket': 'bool',
'*reconnect': 'int' },
'base': 'ChardevCommon' }
##
@ -281,8 +287,9 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddressLegacy',
'*local' : 'SocketAddressLegacy' },
{ 'struct': 'ChardevUdp',
'data': { 'remote': 'SocketAddressLegacy',
'*local': 'SocketAddressLegacy' },
'base': 'ChardevCommon' }
##
@ -294,7 +301,8 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' },
{ 'struct': 'ChardevMux',
'data': { 'chardev': 'str' },
'base': 'ChardevCommon' }
##
@ -308,7 +316,8 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' },
{ 'struct': 'ChardevStdio',
'data': { '*signal': 'bool' },
'base': 'ChardevCommon' }
@ -321,9 +330,10 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' },
'base': 'ChardevCommon' }
# TODO: 'if': 'defined(CONFIG_SPICE)'
{ 'struct': 'ChardevSpiceChannel',
'data': { 'type': 'str' },
'base': 'ChardevCommon',
'if': 'defined(CONFIG_SPICE)' }
##
# @ChardevSpicePort:
@ -334,9 +344,10 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' },
'base': 'ChardevCommon' }
# TODO: 'if': 'defined(CONFIG_SPICE)'
{ 'struct': 'ChardevSpicePort',
'data': { 'fqdn': 'str' },
'base': 'ChardevCommon',
'if': 'defined(CONFIG_SPICE)' }
##
# @ChardevVC:
@ -350,10 +361,11 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevVC', 'data': { '*width' : 'int',
'*height' : 'int',
'*cols' : 'int',
'*rows' : 'int' },
{ 'struct': 'ChardevVC',
'data': { '*width': 'int',
'*height': 'int',
'*cols': 'int',
'*rows': 'int' },
'base': 'ChardevCommon' }
##
@ -365,7 +377,8 @@
#
# Since: 1.5
##
{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' },
{ 'struct': 'ChardevRingbuf',
'data': { '*size': 'int' },
'base': 'ChardevCommon' }
##
@ -375,29 +388,30 @@
#
# Since: 1.4 (testdev since 2.2, wctablet since 2.9)
##
{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
'serial' : 'ChardevHostdev',
'parallel': 'ChardevHostdev',
'pipe' : 'ChardevHostdev',
'socket' : 'ChardevSocket',
'udp' : 'ChardevUdp',
'pty' : 'ChardevCommon',
'null' : 'ChardevCommon',
'mux' : 'ChardevMux',
'msmouse': 'ChardevCommon',
'wctablet' : 'ChardevCommon',
'braille': 'ChardevCommon',
'testdev': 'ChardevCommon',
'stdio' : 'ChardevStdio',
'console': 'ChardevCommon',
'spicevmc': 'ChardevSpiceChannel',
# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
'spiceport': 'ChardevSpicePort',
# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
'vc' : 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
# next one is just for compatibility
'memory' : 'ChardevRingbuf' } }
{ 'union': 'ChardevBackend',
'data': { 'file': 'ChardevFile',
'serial': 'ChardevHostdev',
'parallel': 'ChardevHostdev',
'pipe': 'ChardevHostdev',
'socket': 'ChardevSocket',
'udp': 'ChardevUdp',
'pty': 'ChardevCommon',
'null': 'ChardevCommon',
'mux': 'ChardevMux',
'msmouse': 'ChardevCommon',
'wctablet': 'ChardevCommon',
'braille': 'ChardevCommon',
'testdev': 'ChardevCommon',
'stdio': 'ChardevStdio',
'console': 'ChardevCommon',
'spicevmc': { 'type': 'ChardevSpiceChannel',
'if': 'defined(CONFIG_SPICE)' },
'spiceport': { 'type': 'ChardevSpicePort',
'if': 'defined(CONFIG_SPICE)' },
'vc': 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
# next one is just for compatibility
'memory': 'ChardevRingbuf' } }
##
# @ChardevReturn:
@ -409,7 +423,8 @@
#
# Since: 1.4
##
{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
{ 'struct' : 'ChardevReturn',
'data': { '*pty': 'str' } }
##
# @chardev-add:
@ -442,8 +457,9 @@
# <- { "return": { "pty" : "/dev/pty/42" } }
#
##
{ 'command': 'chardev-add', 'data': {'id' : 'str',
'backend' : 'ChardevBackend' },
{ 'command': 'chardev-add',
'data': { 'id': 'str',
'backend': 'ChardevBackend' },
'returns': 'ChardevReturn' }
##
@ -482,8 +498,9 @@
# <- {"return": {}}
#
##
{ 'command': 'chardev-change', 'data': {'id' : 'str',
'backend' : 'ChardevBackend' },
{ 'command': 'chardev-change',
'data': { 'id': 'str',
'backend': 'ChardevBackend' },
'returns': 'ChardevReturn' }
##
@ -503,7 +520,8 @@
# <- { "return": {} }
#
##
{ 'command': 'chardev-remove', 'data': {'id': 'str'} }
{ 'command': 'chardev-remove',
'data': { 'id': 'str' } }
##
# @chardev-send-break:
@ -522,7 +540,8 @@
# <- { "return": {} }
#
##
{ 'command': 'chardev-send-break', 'data': {'id': 'str'} }
{ 'command': 'chardev-send-break',
'data': { 'id': 'str' } }
##
# @VSERPORT_CHANGE:
@ -543,4 +562,5 @@
#
##
{ 'event': 'VSERPORT_CHANGE',
'data': { 'id': 'str', 'open': 'bool' } }
'data': { 'id': 'str',
'open': 'bool' } }

View File

@ -1257,7 +1257,8 @@
# Since: 2.9
##
{ 'command': 'xen-set-replication',
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
'if': 'defined(CONFIG_REPLICATION)' }
##
# @ReplicationStatus:
@ -1272,7 +1273,8 @@
# Since: 2.9
##
{ 'struct': 'ReplicationStatus',
'data': { 'error': 'bool', '*desc': 'str' } }
'data': { 'error': 'bool', '*desc': 'str' },
'if': 'defined(CONFIG_REPLICATION)' }
##
# @query-xen-replication-status:
@ -1289,7 +1291,8 @@
# Since: 2.9
##
{ 'command': 'query-xen-replication-status',
'returns': 'ReplicationStatus' }
'returns': 'ReplicationStatus',
'if': 'defined(CONFIG_REPLICATION)' }
##
# @xen-colo-do-checkpoint:
@ -1305,7 +1308,8 @@
#
# Since: 2.9
##
{ 'command': 'xen-colo-do-checkpoint' }
{ 'command': 'xen-colo-do-checkpoint',
'if': 'defined(CONFIG_REPLICATION)' }
##
# @COLOStatus:
@ -1356,7 +1360,8 @@
#
# Since: 3.0
##
{ 'command': 'migrate-recover', 'data': { 'uri': 'str' },
{ 'command': 'migrate-recover',
'data': { 'uri': 'str' },
'allow-oob': true }
##

View File

@ -2385,7 +2385,9 @@
# <- { "return": { "fdset-id": 1, "fd": 3 } }
#
##
{ 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'},
{ 'command': 'add-fd',
'data': { '*fdset-id': 'int',
'*opaque': 'str' },
'returns': 'AddfdInfo' }
##
@ -2657,7 +2659,8 @@
# }
#
##
{'command': 'query-command-line-options', 'data': { '*option': 'str' },
{'command': 'query-command-line-options',
'data': { '*option': 'str' },
'returns': ['CommandLineOptionInfo'],
'allow-preconfig': true }

View File

@ -657,7 +657,8 @@
# }
#
##
{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
{ 'command': 'query-rx-filter',
'data': { '*name': 'str' },
'returns': ['RxFilterInfo'] }
##

View File

@ -562,19 +562,20 @@ static void qobject_input_type_number_keyval(Visitor *v, const char *name,
{
QObjectInputVisitor *qiv = to_qiv(v);
const char *str = qobject_input_get_keyval(qiv, name, errp);
char *endp;
double val;
if (!str) {
return;
}
errno = 0;
*obj = strtod(str, &endp);
if (errno || endp == str || *endp || !isfinite(*obj)) {
if (qemu_strtod_finite(str, NULL, &val)) {
/* TODO report -ERANGE more nicely */
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "number");
return;
}
*obj = val;
}
static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,

View File

@ -4,10 +4,10 @@
* Copyright Red Hat, Inc. 2012-2016
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
* David Hildenbrand <david@redhat.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"
@ -18,20 +18,42 @@
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qnull.h"
#include "qemu/option.h"
#include "qemu/queue.h"
#include "qemu/range.h"
#include "qemu/cutils.h"
typedef enum ListMode {
/* no list parsing active / no list expected */
LM_NONE,
/* we have an unparsed string remaining */
LM_UNPARSED,
/* we have an unfinished int64 range */
LM_INT64_RANGE,
/* we have an unfinished uint64 range */
LM_UINT64_RANGE,
/* we have parsed the string completely and no range is remaining */
LM_END,
} ListMode;
/* protect against DOS attacks, limit the amount of elements per range */
#define RANGE_MAX_ELEMENTS 65536
typedef union RangeElement {
int64_t i64;
uint64_t u64;
} RangeElement;
struct StringInputVisitor
{
Visitor visitor;
GList *ranges;
GList *cur_range;
int64_t cur;
/* List parsing state */
ListMode lm;
RangeElement rangeNext;
RangeElement rangeEnd;
const char *unparsed_string;
void *list;
/* The original string to parse */
const char *string;
void *list; /* Only needed for sanity checking the caller */
};
static StringInputVisitor *to_siv(Visitor *v)
@ -39,136 +61,42 @@ static StringInputVisitor *to_siv(Visitor *v)
return container_of(v, StringInputVisitor, visitor);
}
static void free_range(void *range, void *dummy)
{
g_free(range);
}
static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
{
char *str = (char *) siv->string;
long long start, end;
Range *cur;
char *endptr;
if (siv->ranges) {
return 0;
}
if (!*str) {
return 0;
}
do {
errno = 0;
start = strtoll(str, &endptr, 0);
if (errno == 0 && endptr > str) {
if (*endptr == '\0') {
cur = g_malloc0(sizeof(*cur));
range_set_bounds(cur, start, start);
siv->ranges = range_list_insert(siv->ranges, cur);
cur = NULL;
str = NULL;
} else if (*endptr == '-') {
str = endptr + 1;
errno = 0;
end = strtoll(str, &endptr, 0);
if (errno == 0 && endptr > str && start <= end &&
(start > INT64_MAX - 65536 ||
end < start + 65536)) {
if (*endptr == '\0') {
cur = g_malloc0(sizeof(*cur));
range_set_bounds(cur, start, end);
siv->ranges = range_list_insert(siv->ranges, cur);
cur = NULL;
str = NULL;
} else if (*endptr == ',') {
str = endptr + 1;
cur = g_malloc0(sizeof(*cur));
range_set_bounds(cur, start, end);
siv->ranges = range_list_insert(siv->ranges, cur);
cur = NULL;
} else {
goto error;
}
} else {
goto error;
}
} else if (*endptr == ',') {
str = endptr + 1;
cur = g_malloc0(sizeof(*cur));
range_set_bounds(cur, start, start);
siv->ranges = range_list_insert(siv->ranges, cur);
cur = NULL;
} else {
goto error;
}
} else {
goto error;
}
} while (str);
return 0;
error:
g_list_foreach(siv->ranges, free_range, NULL);
g_list_free(siv->ranges);
siv->ranges = NULL;
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
"an int64 value or range");
return -1;
}
static void
start_list(Visitor *v, const char *name, GenericList **list, size_t size,
Error **errp)
static void start_list(Visitor *v, const char *name, GenericList **list,
size_t size, Error **errp)
{
StringInputVisitor *siv = to_siv(v);
/* We don't support visits without a list */
assert(list);
assert(siv->lm == LM_NONE);
siv->list = list;
siv->unparsed_string = siv->string;
if (parse_str(siv, name, errp) < 0) {
*list = NULL;
return;
}
siv->cur_range = g_list_first(siv->ranges);
if (siv->cur_range) {
Range *r = siv->cur_range->data;
if (r) {
siv->cur = range_lob(r);
if (!siv->string[0]) {
if (list) {
*list = NULL;
}
*list = g_malloc0(size);
siv->lm = LM_END;
} else {
*list = NULL;
if (list) {
*list = g_malloc0(size);
}
siv->lm = LM_UNPARSED;
}
}
static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
{
StringInputVisitor *siv = to_siv(v);
Range *r;
if (!siv->ranges || !siv->cur_range) {
switch (siv->lm) {
case LM_END:
return NULL;
}
r = siv->cur_range->data;
if (!r) {
return NULL;
}
if (!range_contains(r, siv->cur)) {
siv->cur_range = g_list_next(siv->cur_range);
if (!siv->cur_range) {
return NULL;
}
r = siv->cur_range->data;
if (!r) {
return NULL;
}
siv->cur = range_lob(r);
case LM_INT64_RANGE:
case LM_UINT64_RANGE:
case LM_UNPARSED:
/* we have an unparsed string or something left in a range */
break;
default:
abort();
}
tail->next = g_malloc0(size);
@ -178,88 +106,208 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
static void check_list(Visitor *v, Error **errp)
{
const StringInputVisitor *siv = to_siv(v);
Range *r;
GList *cur_range;
if (!siv->ranges || !siv->cur_range) {
switch (siv->lm) {
case LM_INT64_RANGE:
case LM_UINT64_RANGE:
case LM_UNPARSED:
error_setg(errp, "Fewer list elements expected");
return;
}
r = siv->cur_range->data;
if (!r) {
case LM_END:
return;
default:
abort();
}
if (!range_contains(r, siv->cur)) {
cur_range = g_list_next(siv->cur_range);
if (!cur_range) {
return;
}
r = cur_range->data;
if (!r) {
return;
}
}
error_setg(errp, "Range contains too many values");
}
static void end_list(Visitor *v, void **obj)
{
StringInputVisitor *siv = to_siv(v);
assert(siv->lm != LM_NONE);
assert(siv->list == obj);
siv->list = NULL;
siv->unparsed_string = NULL;
siv->lm = LM_NONE;
}
static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
{
const char *endptr;
int64_t start, end;
/* parse a simple int64 or range */
if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
return -EINVAL;
}
end = start;
switch (endptr[0]) {
case '\0':
siv->unparsed_string = endptr;
break;
case ',':
siv->unparsed_string = endptr + 1;
break;
case '-':
/* parse the end of the range */
if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
return -EINVAL;
}
if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
return -EINVAL;
}
switch (endptr[0]) {
case '\0':
siv->unparsed_string = endptr;
break;
case ',':
siv->unparsed_string = endptr + 1;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
/* we have a proper range (with maybe only one element) */
siv->lm = LM_INT64_RANGE;
siv->rangeNext.i64 = start;
siv->rangeEnd.i64 = end;
return 0;
}
static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
Error **errp)
{
StringInputVisitor *siv = to_siv(v);
int64_t val;
if (parse_str(siv, name, errp) < 0) {
switch (siv->lm) {
case LM_NONE:
/* just parse a simple int64, bail out if not completely consumed */
if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
name ? name : "null", "int64");
return;
}
*obj = val;
return;
}
if (!siv->ranges) {
goto error;
}
if (!siv->cur_range) {
Range *r;
siv->cur_range = g_list_first(siv->ranges);
if (!siv->cur_range) {
goto error;
case LM_UNPARSED:
if (try_parse_int64_list_entry(siv, obj)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
"list of int64 values or ranges");
return;
}
assert(siv->lm == LM_INT64_RANGE);
/* fall through */
case LM_INT64_RANGE:
/* return the next element in the range */
assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
*obj = siv->rangeNext.i64++;
r = siv->cur_range->data;
if (!r) {
goto error;
if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
/* end of range, check if there is more to parse */
siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
}
return;
case LM_END:
error_setg(errp, "Fewer list elements expected");
return;
default:
abort();
}
}
siv->cur = range_lob(r);
static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
{
const char *endptr;
uint64_t start, end;
/* parse a simple uint64 or range */
if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
return -EINVAL;
}
end = start;
switch (endptr[0]) {
case '\0':
siv->unparsed_string = endptr;
break;
case ',':
siv->unparsed_string = endptr + 1;
break;
case '-':
/* parse the end of the range */
if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
return -EINVAL;
}
if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
return -EINVAL;
}
switch (endptr[0]) {
case '\0':
siv->unparsed_string = endptr;
break;
case ',':
siv->unparsed_string = endptr + 1;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
*obj = siv->cur;
siv->cur++;
return;
error:
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
"an int64 value or range");
/* we have a proper range (with maybe only one element) */
siv->lm = LM_UINT64_RANGE;
siv->rangeNext.u64 = start;
siv->rangeEnd.u64 = end;
return 0;
}
static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
Error **errp)
{
/* FIXME: parse_type_int64 mishandles values over INT64_MAX */
int64_t i;
Error *err = NULL;
parse_type_int64(v, name, &i, &err);
if (err) {
error_propagate(errp, err);
} else {
*obj = i;
StringInputVisitor *siv = to_siv(v);
uint64_t val;
switch (siv->lm) {
case LM_NONE:
/* just parse a simple uint64, bail out if not completely consumed */
if (qemu_strtou64(siv->string, NULL, 0, &val)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
"uint64");
return;
}
*obj = val;
return;
case LM_UNPARSED:
if (try_parse_uint64_list_entry(siv, obj)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
"list of uint64 values or ranges");
return;
}
assert(siv->lm == LM_UINT64_RANGE);
/* fall through */
case LM_UINT64_RANGE:
/* return the next element in the range */
assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
*obj = siv->rangeNext.u64++;
if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
/* end of range, check if there is more to parse */
siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
}
return;
case LM_END:
error_setg(errp, "Fewer list elements expected");
return;
default:
abort();
}
}
@ -270,6 +318,7 @@ static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
Error *err = NULL;
uint64_t val;
assert(siv->lm == LM_NONE);
parse_option_size(name, siv->string, &val, &err);
if (err) {
error_propagate(errp, err);
@ -284,6 +333,7 @@ static void parse_type_bool(Visitor *v, const char *name, bool *obj,
{
StringInputVisitor *siv = to_siv(v);
assert(siv->lm == LM_NONE);
if (!strcasecmp(siv->string, "on") ||
!strcasecmp(siv->string, "yes") ||
!strcasecmp(siv->string, "true")) {
@ -306,6 +356,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
{
StringInputVisitor *siv = to_siv(v);
assert(siv->lm == LM_NONE);
*obj = g_strdup(siv->string);
}
@ -313,12 +364,10 @@ static void parse_type_number(Visitor *v, const char *name, double *obj,
Error **errp)
{
StringInputVisitor *siv = to_siv(v);
char *endp = (char *) siv->string;
double val;
errno = 0;
val = strtod(siv->string, &endp);
if (errno || endp == siv->string || *endp) {
assert(siv->lm == LM_NONE);
if (qemu_strtod_finite(siv->string, NULL, &val)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"number");
return;
@ -332,9 +381,10 @@ static void parse_type_null(Visitor *v, const char *name, QNull **obj,
{
StringInputVisitor *siv = to_siv(v);
assert(siv->lm == LM_NONE);
*obj = NULL;
if (!siv->string || siv->string[0]) {
if (siv->string[0]) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"null");
return;
@ -347,8 +397,6 @@ static void string_input_free(Visitor *v)
{
StringInputVisitor *siv = to_siv(v);
g_list_foreach(siv->ranges, free_range, NULL);
g_list_free(siv->ranges);
g_free(siv);
}
@ -374,5 +422,6 @@ Visitor *string_input_visitor_new(const char *str)
v->visitor.free = string_input_free;
v->string = str;
v->lm = LM_NONE;
return &v->visitor;
}

View File

@ -76,8 +76,9 @@
#
# Since: 1.5
##
{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
'*cancel-path' : 'str'} }
{ 'struct': 'TPMPassthroughOptions',
'data': { '*path': 'str',
'*cancel-path': 'str' } }
##
# @TPMEmulatorOptions:

View File

@ -598,7 +598,8 @@
# Notes: An empty password in this command will set the password to the empty
# string. Existing clients are unaffected by executing this command.
##
{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
{ 'command': 'change-vnc-password',
'data': { 'password': 'str' },
'if': 'defined(CONFIG_VNC)' }
##

View File

@ -288,6 +288,11 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict)
goto out;
}
if (qdict_haskey(dict, qstring_get_str(key))) {
parse_error(ctxt, token, "duplicate key");
goto out;
}
qdict_put_obj(dict, qstring_get_str(key), value);
qobject_unref(key);

View File

@ -588,11 +588,11 @@ def discriminator_find_enum_define(expr):
if not base_members:
return None
discriminator_type = base_members.get(discriminator)
if not discriminator_type:
discriminator_value = base_members.get(discriminator)
if not discriminator_value:
return None
return enum_types.get(discriminator_type)
return enum_types.get(discriminator_value['type'])
# Names must be letters, numbers, -, and _. They must start with letter,
@ -704,8 +704,10 @@ def check_type(info, source, value, allow_array=False,
% (source, key))
# Todo: allow dictionaries to represent default values of
# an optional argument.
check_type(info, "Member '%s' of %s" % (key, source), arg,
allow_array=True,
check_known_keys(info, "member '%s' of %s" % (key, source),
arg, ['type'], ['if'])
check_type(info, "Member '%s' of %s" % (key, source),
arg['type'], allow_array=True,
allow_metas=['built-in', 'union', 'alternate', 'struct',
'enum'])
@ -740,6 +742,10 @@ def check_event(expr, info):
allow_metas=meta)
def enum_get_names(expr):
return [e['name'] for e in expr['data']]
def check_union(expr, info):
name = expr['union']
base = expr.get('base')
@ -772,13 +778,17 @@ def check_union(expr, info):
# member of the base struct.
check_name(info, "Discriminator of flat union '%s'" % name,
discriminator)
discriminator_type = base_members.get(discriminator)
if not discriminator_type:
discriminator_value = base_members.get(discriminator)
if not discriminator_value:
raise QAPISemError(info,
"Discriminator '%s' is not a member of base "
"struct '%s'"
% (discriminator, base))
enum_define = enum_types.get(discriminator_type)
if discriminator_value.get('if'):
raise QAPISemError(info, 'The discriminator %s.%s for union %s '
'must not be conditional' %
(base, discriminator, name))
enum_define = enum_types.get(discriminator_value['type'])
allow_metas = ['struct']
# Do not allow string discriminator
if not enum_define:
@ -792,14 +802,17 @@ def check_union(expr, info):
for (key, value) in members.items():
check_name(info, "Member of union '%s'" % name, key)
check_known_keys(info, "member '%s' of union '%s'" % (key, name),
value, ['type'], ['if'])
# Each value must name a known type
check_type(info, "Member '%s' of union '%s'" % (key, name),
value, allow_array=not base, allow_metas=allow_metas)
value['type'],
allow_array=not base, allow_metas=allow_metas)
# If the discriminator names an enum type, then all members
# of 'data' must also be members of the enum type.
if enum_define:
if key not in enum_define['data']:
if key not in enum_get_names(enum_define):
raise QAPISemError(info,
"Discriminator value '%s' is not found in "
"enum '%s'"
@ -818,20 +831,23 @@ def check_alternate(expr, info):
"in 'data'" % name)
for (key, value) in members.items():
check_name(info, "Member of alternate '%s'" % name, key)
check_known_keys(info,
"member '%s' of alternate '%s'" % (key, name),
value, ['type'], ['if'])
typ = value['type']
# Ensure alternates have no type conflicts.
check_type(info, "Member '%s' of alternate '%s'" % (key, name),
value,
check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
allow_metas=['built-in', 'union', 'struct', 'enum'])
qtype = find_alternate_member_qtype(value)
qtype = find_alternate_member_qtype(typ)
if not qtype:
raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
"type '%s'" % (name, key, value))
"type '%s'" % (name, key, typ))
conflicting = set([qtype])
if qtype == 'QTYPE_QSTRING':
enum_expr = enum_types.get(value)
enum_expr = enum_types.get(typ)
if enum_expr:
for v in enum_expr['data']:
for v in enum_get_names(enum_expr):
if v in ['on', 'off']:
conflicting.add('QTYPE_QBOOL')
if re.match(r'[-+0-9.]', v): # lazy, could be tightened
@ -849,7 +865,7 @@ def check_alternate(expr, info):
def check_enum(expr, info):
name = expr['enum']
members = expr.get('data')
members = expr['data']
prefix = expr.get('prefix')
if not isinstance(members, list):
@ -858,8 +874,12 @@ def check_enum(expr, info):
if prefix is not None and not isinstance(prefix, str):
raise QAPISemError(info,
"Enum '%s' requires a string for 'prefix'" % name)
for member in members:
check_name(info, "Member of enum '%s'" % name, member,
source = "dictionary member of enum '%s'" % name
check_known_keys(info, source, member, ['name'], ['if'])
check_if(member, info)
check_name(info, "Member of enum '%s'" % name, member['name'],
enum_member=True)
@ -873,6 +893,24 @@ def check_struct(expr, info):
allow_metas=['struct'])
def check_known_keys(info, source, keys, required, optional):
def pprint(elems):
return ', '.join("'" + e + "'" for e in sorted(elems))
missing = set(required) - set(keys)
if missing:
raise QAPISemError(info, "Key%s %s %s missing from %s"
% ('s' if len(missing) > 1 else '', pprint(missing),
'are' if len(missing) > 1 else 'is', source))
allowed = set(required + optional)
unknown = set(keys) - allowed
if unknown:
raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
% ('s' if len(unknown) > 1 else '', pprint(unknown),
source, pprint(allowed)))
def check_keys(expr_elem, meta, required, optional=[]):
expr = expr_elem['expr']
info = expr_elem['info']
@ -880,10 +918,9 @@ def check_keys(expr_elem, meta, required, optional=[]):
if not isinstance(name, str):
raise QAPISemError(info, "'%s' key must have a string value" % meta)
required = required + [meta]
source = "%s '%s'" % (meta, name)
check_known_keys(info, source, expr.keys(), required, optional)
for (key, value) in expr.items():
if key not in required and key not in optional:
raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
% (key, meta, name))
if key in ['gen', 'success-response'] and value is not False:
raise QAPISemError(info,
"'%s' of %s '%s' should only use false value"
@ -895,10 +932,20 @@ def check_keys(expr_elem, meta, required, optional=[]):
% (key, meta, name))
if key == 'if':
check_if(expr, info)
for key in required:
if key not in expr:
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
% (key, meta, name))
def normalize_enum(expr):
if isinstance(expr['data'], list):
expr['data'] = [m if isinstance(m, dict) else {'name': m}
for m in expr['data']]
def normalize_members(members):
if isinstance(members, OrderedDict):
for key, arg in members.items():
if isinstance(arg, dict):
continue
members[key] = {'type': arg}
def check_exprs(exprs):
@ -924,27 +971,34 @@ def check_exprs(exprs):
if 'enum' in expr:
meta = 'enum'
check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
normalize_enum(expr)
enum_types[expr[meta]] = expr
elif 'union' in expr:
meta = 'union'
check_keys(expr_elem, 'union', ['data'],
['base', 'discriminator', 'if'])
normalize_members(expr.get('base'))
normalize_members(expr['data'])
union_types[expr[meta]] = expr
elif 'alternate' in expr:
meta = 'alternate'
check_keys(expr_elem, 'alternate', ['data'], ['if'])
normalize_members(expr['data'])
elif 'struct' in expr:
meta = 'struct'
check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
normalize_members(expr['data'])
struct_types[expr[meta]] = expr
elif 'command' in expr:
meta = 'command'
check_keys(expr_elem, 'command', [],
['data', 'returns', 'gen', 'success-response',
'boxed', 'allow-oob', 'allow-preconfig', 'if'])
normalize_members(expr.get('data'))
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
normalize_members(expr.get('data'))
else:
raise QAPISemError(expr_elem['info'],
"Expression is missing metatype")
@ -1063,7 +1117,7 @@ class QAPISchemaVisitor(object):
def visit_builtin_type(self, name, info, json_type):
pass
def visit_enum_type(self, name, info, ifcond, values, prefix):
def visit_enum_type(self, name, info, ifcond, members, prefix):
pass
def visit_array_type(self, name, info, ifcond, element_type):
@ -1161,22 +1215,22 @@ class QAPISchemaBuiltinType(QAPISchemaType):
class QAPISchemaEnumType(QAPISchemaType):
def __init__(self, name, info, doc, ifcond, values, prefix):
def __init__(self, name, info, doc, ifcond, members, prefix):
QAPISchemaType.__init__(self, name, info, doc, ifcond)
for v in values:
assert isinstance(v, QAPISchemaMember)
v.set_owner(name)
for m in members:
assert isinstance(m, QAPISchemaMember)
m.set_owner(name)
assert prefix is None or isinstance(prefix, str)
self.values = values
self.members = members
self.prefix = prefix
def check(self, schema):
QAPISchemaType.check(self, schema)
seen = {}
for v in self.values:
v.check_clash(self.info, seen)
for m in self.members:
m.check_clash(self.info, seen)
if self.doc:
self.doc.connect_member(v)
self.doc.connect_member(m)
def is_implicit(self):
# See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
@ -1186,14 +1240,14 @@ class QAPISchemaEnumType(QAPISchemaType):
return c_name(self.name)
def member_names(self):
return [v.name for v in self.values]
return [m.name for m in self.members]
def json_type(self):
return 'string'
def visit(self, visitor):
visitor.visit_enum_type(self.name, self.info, self.ifcond,
self.member_names(), self.prefix)
self.members, self.prefix)
class QAPISchemaArrayType(QAPISchemaType):
@ -1318,9 +1372,10 @@ class QAPISchemaObjectType(QAPISchemaType):
class QAPISchemaMember(object):
role = 'member'
def __init__(self, name):
def __init__(self, name, ifcond=None):
assert isinstance(name, str)
self.name = name
self.ifcond = listify_cond(ifcond)
self.owner = None
def set_owner(self, name):
@ -1361,8 +1416,8 @@ class QAPISchemaMember(object):
class QAPISchemaObjectTypeMember(QAPISchemaMember):
def __init__(self, name, typ, optional):
QAPISchemaMember.__init__(self, name)
def __init__(self, name, typ, optional, ifcond=None):
QAPISchemaMember.__init__(self, name, ifcond)
assert isinstance(typ, str)
assert isinstance(optional, bool)
self._type_name = typ
@ -1403,9 +1458,9 @@ class QAPISchemaObjectTypeVariants(object):
if self._tag_name: # flat union
# branches that are not explicitly covered get an empty type
cases = set([v.name for v in self.variants])
for val in self.tag_member.type.values:
if val.name not in cases:
v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
for m in self.tag_member.type.members:
if m.name not in cases:
v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
v.set_owner(self.tag_member.owner)
self.variants.append(v)
for v in self.variants:
@ -1428,8 +1483,8 @@ class QAPISchemaObjectTypeVariants(object):
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
role = 'branch'
def __init__(self, name, typ):
QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
def __init__(self, name, typ, ifcond=None):
QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
class QAPISchemaAlternateType(QAPISchemaType):
@ -1620,14 +1675,16 @@ class QAPISchema(object):
self.the_empty_object_type = QAPISchemaObjectType(
'q_empty', None, None, None, None, [], None)
self._def_entity(self.the_empty_object_type)
qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
'qstring', 'qdict', 'qlist',
'qbool'])
qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
'qbool']
qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
qtype_values, 'QTYPE'))
def _make_enum_members(self, values):
return [QAPISchemaMember(v) for v in values]
return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
def _make_implicit_enum_type(self, name, info, ifcond, values):
# See also QAPISchemaObjectTypeMember._pretty_owner()
@ -1674,7 +1731,7 @@ class QAPISchema(object):
name, info, doc, ifcond,
self._make_enum_members(data), prefix))
def _make_member(self, name, typ, info):
def _make_member(self, name, typ, ifcond, info):
optional = False
if name.startswith('*'):
name = name[1:]
@ -1682,10 +1739,10 @@ class QAPISchema(object):
if isinstance(typ, list):
assert len(typ) == 1
typ = self._make_array_type(typ[0], info)
return QAPISchemaObjectTypeMember(name, typ, optional)
return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
def _make_members(self, data, info):
return [self._make_member(key, value, info)
return [self._make_member(key, value['type'], value.get('if'), info)
for (key, value) in data.items()]
def _def_struct_type(self, expr, info, doc):
@ -1697,17 +1754,17 @@ class QAPISchema(object):
self._make_members(data, info),
None))
def _make_variant(self, case, typ):
return QAPISchemaObjectTypeVariant(case, typ)
def _make_variant(self, case, typ, ifcond):
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
def _make_simple_variant(self, case, typ, info):
def _make_simple_variant(self, case, typ, ifcond, info):
if isinstance(typ, list):
assert len(typ) == 1
typ = self._make_array_type(typ[0], info)
typ = self._make_implicit_object_type(
typ, info, None, self.lookup_type(typ),
'wrapper', [self._make_member('data', typ, info)])
return QAPISchemaObjectTypeVariant(case, typ)
'wrapper', [self._make_member('data', typ, None, info)])
return QAPISchemaObjectTypeVariant(case, typ, ifcond)
def _def_union_type(self, expr, info, doc):
name = expr['union']
@ -1721,14 +1778,15 @@ class QAPISchema(object):
name, info, doc, ifcond,
'base', self._make_members(base, info))
if tag_name:
variants = [self._make_variant(key, value)
variants = [self._make_variant(key, value['type'], value.get('if'))
for (key, value) in data.items()]
members = []
else:
variants = [self._make_simple_variant(key, value, info)
variants = [self._make_simple_variant(key, value['type'],
value.get('if'), info)
for (key, value) in data.items()]
typ = self._make_implicit_enum_type(name, info, ifcond,
[v.name for v in variants])
enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
tag_member = QAPISchemaObjectTypeMember('type', typ, False)
members = [tag_member]
self._def_entity(
@ -1741,7 +1799,7 @@ class QAPISchema(object):
name = expr['alternate']
data = expr['data']
ifcond = expr.get('if')
variants = [self._make_variant(key, value)
variants = [self._make_variant(key, value['type'], value.get('if'))
for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
self._def_entity(
@ -2012,19 +2070,21 @@ def _wrap_ifcond(ifcond, before, after):
return out
def gen_enum_lookup(name, values, prefix=None):
def gen_enum_lookup(name, members, prefix=None):
ret = mcgen('''
const QEnumLookup %(c_name)s_lookup = {
.array = (const char *const[]) {
''',
c_name=c_name(name))
for value in values:
index = c_enum_const(name, value, prefix)
for m in members:
ret += gen_if(m.ifcond)
index = c_enum_const(name, m.name, prefix)
ret += mcgen('''
[%(index)s] = "%(value)s",
[%(index)s] = "%(name)s",
''',
index=index, value=value)
index=index, name=m.name)
ret += gen_endif(m.ifcond)
ret += mcgen('''
},
@ -2035,9 +2095,9 @@ const QEnumLookup %(c_name)s_lookup = {
return ret
def gen_enum(name, values, prefix=None):
def gen_enum(name, members, prefix=None):
# append automatically generated _MAX value
enum_values = values + ['_MAX']
enum_members = members + [QAPISchemaMember('_MAX')]
ret = mcgen('''
@ -2045,14 +2105,13 @@ typedef enum %(c_name)s {
''',
c_name=c_name(name))
i = 0
for value in enum_values:
for m in enum_members:
ret += gen_if(m.ifcond)
ret += mcgen('''
%(c_enum)s = %(i)d,
%(c_enum)s,
''',
c_enum=c_enum_const(name, value, prefix),
i=i)
i += 1
c_enum=c_enum_const(name, m.name, prefix))
ret += gen_endif(m.ifcond)
ret += mcgen('''
} %(c_name)s;

View File

@ -126,19 +126,27 @@ def texi_body(doc):
return texi_format(doc.body.text)
def texi_enum_value(value):
def texi_if(ifcond, prefix='\n', suffix='\n'):
"""Format the #if condition"""
if not ifcond:
return ''
return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix)
def texi_enum_value(value, desc, suffix):
"""Format a table of members item for an enumeration value"""
return '@item @code{%s}\n' % value.name
return '@item @code{%s}\n%s%s' % (
value.name, desc, texi_if(value.ifcond, prefix='@*'))
def texi_member(member, suffix=''):
def texi_member(member, desc, suffix):
"""Format a table of members item for an object type member"""
typ = member.type.doc_type()
membertype = ': ' + typ if typ else ''
return '@item @code{%s%s}%s%s\n' % (
return '@item @code{%s%s}%s%s\n%s%s' % (
member.name, membertype,
' (optional)' if member.optional else '',
suffix)
suffix, desc, texi_if(member.ifcond, prefix='@*'))
def texi_members(doc, what, base, variants, member_func):
@ -155,17 +163,17 @@ def texi_members(doc, what, base, variants, member_func):
desc = 'One of ' + members_text + '\n'
else:
desc = 'Not documented\n'
items += member_func(section.member) + desc
items += member_func(section.member, desc, suffix='')
if base:
items += '@item The members of @code{%s}\n' % base.doc_type()
if variants:
for v in variants.variants:
when = ' when @code{%s} is @t{"%s"}' % (
variants.tag_member.name, v.name)
when = ' when @code{%s} is @t{"%s"}%s' % (
variants.tag_member.name, v.name, texi_if(v.ifcond, " (", ")"))
if v.type.is_implicit():
assert not v.type.base and not v.type.variants
for m in v.type.local_members:
items += member_func(m, when)
items += member_func(m, desc='', suffix=when)
else:
items += '@item The members of @code{%s}%s\n' % (
v.type.doc_type(), when)
@ -185,8 +193,7 @@ def texi_sections(doc, ifcond):
body += texi_example(section.text)
else:
body += texi_format(section.text)
if ifcond:
body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
body += texi_if(ifcond, suffix='')
return body
@ -206,7 +213,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
def write(self, output_dir):
self._gen.write(output_dir, self._prefix + 'qapi-doc.texi')
def visit_enum_type(self, name, info, ifcond, values, prefix):
def visit_enum_type(self, name, info, ifcond, members, prefix):
doc = self.cur_doc
self._gen.add(TYPE_FMT(type='Enum',
name=doc.symbol,

View File

@ -143,8 +143,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
QAPISchemaModularCVisitor.__init__(
self, prefix, 'qapi-events',
' * Schema-defined QAPI/QMP events', __doc__)
self._enum_name = c_name(prefix + 'QAPIEvent', protect=False)
self._event_names = []
self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False)
self._event_enum_members = []
def _begin_module(self, name):
types = self._module_basename('qapi-types', name)
@ -170,15 +170,16 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
def visit_end(self):
(genc, genh) = self._module[self._main_module]
genh.add(gen_enum(self._enum_name, self._event_names))
genc.add(gen_enum_lookup(self._enum_name, self._event_names))
genh.add(gen_enum(self._event_enum_name, self._event_enum_members))
genc.add(gen_enum_lookup(self._event_enum_name,
self._event_enum_members))
def visit_event(self, name, info, ifcond, arg_type, boxed):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
self._genc.add(gen_event_send(name, arg_type, boxed,
self._enum_name))
self._event_names.append(name)
self._event_enum_name))
self._event_enum_members.append(QAPISchemaMember(name, ifcond))
def gen_events(schema, output_dir, prefix):

View File

@ -162,6 +162,8 @@ const QLitObject %(c_name)s = %(c_string)s;
ret = {'name': member.name, 'type': self._use_type(member.type)}
if member.optional:
ret['default'] = None
if member.ifcond:
ret = (ret, {'if': member.ifcond})
return ret
def _gen_variants(self, tag_name, variants):
@ -169,13 +171,17 @@ const QLitObject %(c_name)s = %(c_string)s;
'variants': [self._gen_variant(v) for v in variants]}
def _gen_variant(self, variant):
return {'case': variant.name, 'type': self._use_type(variant.type)}
return ({'case': variant.name, 'type': self._use_type(variant.type)},
{'if': variant.ifcond})
def visit_builtin_type(self, name, info, json_type):
self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
def visit_enum_type(self, name, info, ifcond, values, prefix):
self._gen_qlit(name, 'enum', {'values': values}, ifcond)
def visit_enum_type(self, name, info, ifcond, members, prefix):
self._gen_qlit(name, 'enum',
{'values':
[(m.name, {'if': m.ifcond}) for m in members]},
ifcond)
def visit_array_type(self, name, info, ifcond, element_type):
element = self._use_type(element_type)
@ -191,8 +197,9 @@ const QLitObject %(c_name)s = %(c_string)s;
def visit_alternate_type(self, name, info, ifcond, variants):
self._gen_qlit(name, 'alternate',
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]}, ifcond)
{'members': [
({'type': self._use_type(m.type)}, {'if': m.ifcond})
for m in variants.variants]}, ifcond)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):

View File

@ -43,6 +43,7 @@ struct %(c_name)s {
def gen_struct_members(members):
ret = ''
for memb in members:
ret += gen_if(memb.ifcond)
if memb.optional:
ret += mcgen('''
bool has_%(c_name)s;
@ -52,6 +53,7 @@ def gen_struct_members(members):
%(c_type)s %(c_name)s;
''',
c_type=memb.type.c_type(), c_name=c_name(memb.name))
ret += gen_endif(memb.ifcond)
return ret
@ -131,11 +133,13 @@ def gen_variants(variants):
for var in variants.variants:
if var.type.name == 'q_empty':
continue
ret += gen_if(var.ifcond)
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=var.type.c_unboxed_type(),
c_name=c_name(var.name))
ret += gen_endif(var.ifcond)
ret += mcgen('''
} u;
@ -212,10 +216,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
self._genh.add(gen_type_cleanup_decl(name))
self._genc.add(gen_type_cleanup(name))
def visit_enum_type(self, name, info, ifcond, values, prefix):
def visit_enum_type(self, name, info, ifcond, members, prefix):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.preamble_add(gen_enum(name, values, prefix))
self._genc.add(gen_enum_lookup(name, values, prefix))
self._genh.preamble_add(gen_enum(name, members, prefix))
self._genc.add(gen_enum_lookup(name, members, prefix))
def visit_array_type(self, name, info, ifcond, element_type):
with ifcontext(ifcond, self._genh, self._genc):

View File

@ -54,6 +54,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
c_type=base.c_name())
for memb in members:
ret += gen_if(memb.ifcond)
if memb.optional:
ret += mcgen('''
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
@ -73,6 +74,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
ret += mcgen('''
}
''')
ret += gen_endif(memb.ifcond)
if variants:
ret += mcgen('''
@ -84,6 +86,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
case_str = c_enum_const(variants.tag_member.type.name,
var.name,
variants.tag_member.type.prefix)
ret += gen_if(var.ifcond)
if var.type.name == 'q_empty':
# valid variant and nothing to do
ret += mcgen('''
@ -100,6 +103,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
case=case_str,
c_type=var.type.c_name(), c_name=c_name(var.name))
ret += gen_endif(var.ifcond)
ret += mcgen('''
default:
abort();
@ -190,6 +194,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
c_name=c_name(name))
for var in variants.variants:
ret += gen_if(var.ifcond)
ret += mcgen('''
case %(case)s:
''',
@ -217,6 +222,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
ret += mcgen('''
break;
''')
ret += gen_endif(var.ifcond)
ret += mcgen('''
case QTYPE_NONE:
@ -310,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
''',
types=types))
def visit_enum_type(self, name, info, ifcond, values, prefix):
def visit_enum_type(self, name, info, ifcond, members, prefix):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name, scalar=True))
self._genc.add(gen_visit_enum(name))

View File

@ -318,6 +318,7 @@ qapi-schema += alternate-conflict-string.json
qapi-schema += alternate-conflict-bool-string.json
qapi-schema += alternate-conflict-num-string.json
qapi-schema += alternate-empty.json
qapi-schema += alternate-invalid-dict.json
qapi-schema += alternate-nested.json
qapi-schema += alternate-unknown.json
qapi-schema += args-alternate.json
@ -379,10 +380,12 @@ qapi-schema += double-data.json
qapi-schema += double-type.json
qapi-schema += duplicate-key.json
qapi-schema += empty.json
qapi-schema += enum-bad-member.json
qapi-schema += enum-bad-name.json
qapi-schema += enum-bad-prefix.json
qapi-schema += enum-clash-member.json
qapi-schema += enum-dict-member.json
qapi-schema += enum-dict-member-unknown.json
qapi-schema += enum-if-invalid.json
qapi-schema += enum-int-member.json
qapi-schema += enum-member-case.json
qapi-schema += enum-missing-data.json
@ -392,6 +395,7 @@ qapi-schema += escape-too-big.json
qapi-schema += escape-too-short.json
qapi-schema += event-boxed-empty.json
qapi-schema += event-case.json
qapi-schema += event-member-invalid-dict.json
qapi-schema += event-nest-struct.json
qapi-schema += flat-union-array-branch.json
qapi-schema += flat-union-bad-base.json
@ -401,9 +405,11 @@ qapi-schema += flat-union-base-union.json
qapi-schema += flat-union-clash-member.json
qapi-schema += flat-union-empty.json
qapi-schema += flat-union-inline.json
qapi-schema += flat-union-inline-invalid-dict.json
qapi-schema += flat-union-int-branch.json
qapi-schema += flat-union-invalid-branch-key.json
qapi-schema += flat-union-invalid-discriminator.json
qapi-schema += flat-union-invalid-if-discriminator.json
qapi-schema += flat-union-no-base.json
qapi-schema += flat-union-optional-discriminator.json
qapi-schema += flat-union-string-discriminator.json
@ -428,6 +434,7 @@ qapi-schema += missing-comma-list.json
qapi-schema += missing-comma-object.json
qapi-schema += missing-type.json
qapi-schema += nested-struct-data.json
qapi-schema += nested-struct-data-invalid-dict.json
qapi-schema += non-objects.json
qapi-schema += oob-test.json
qapi-schema += allow-preconfig-test.json
@ -458,6 +465,7 @@ qapi-schema += returns-whitelist.json
qapi-schema += struct-base-clash-deep.json
qapi-schema += struct-base-clash.json
qapi-schema += struct-data-invalid.json
qapi-schema += struct-member-invalid-dict.json
qapi-schema += struct-member-invalid.json
qapi-schema += trailing-comma-list.json
qapi-schema += trailing-comma-object.json
@ -469,6 +477,7 @@ qapi-schema += unicode-str.json
qapi-schema += union-base-empty.json
qapi-schema += union-base-no-discriminator.json
qapi-schema += union-branch-case.json
qapi-schema += union-branch-invalid-dict.json
qapi-schema += union-clash-branches.json
qapi-schema += union-empty.json
qapi-schema += union-invalid-base.json

View File

@ -1 +1,2 @@
tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
Valid keys are 'alternate', 'data', 'if'.

View File

@ -0,0 +1 @@
tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'

View File

@ -0,0 +1,4 @@
# exploded member form must have a 'type'
{ 'alternate': 'Alt',
'data': { 'one': 'str',
'two': { 'if': 'foo' } } }

View File

@ -1,5 +1,15 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module comments.json
enum Status ['good', 'bad', 'ugly']
enum Status
member good
member bad
member ugly

View File

@ -1,8 +1,17 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module doc-bad-section.json
enum Enum ['one', 'two']
enum Enum
member one
member two
doc symbol=Enum
body=
== Produces *invalid* texinfo

View File

@ -55,7 +55,9 @@
#
# @two is undocumented
##
{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
{ 'enum': 'Enum', 'data':
[ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ],
'if': 'defined(IFCOND)' }
##
# @Base:
@ -70,7 +72,8 @@
#
# Another paragraph (but no @var: line)
##
{ 'struct': 'Variant1', 'data': { 'var1': 'str' } }
{ 'struct': 'Variant1',
'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } }
##
# @Variant2:
@ -83,13 +86,13 @@
{ 'union': 'Object',
'base': 'Base',
'discriminator': 'base1',
'data': { 'one': 'Variant1', 'two': 'Variant2' } }
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
##
# @SugaredUnion:
##
{ 'union': 'SugaredUnion',
'data': { 'one': 'Variant1', 'two': 'Variant2' } }
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
##
# == Another subsection

View File

@ -1,29 +1,45 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module doc-good.json
enum Enum ['one', 'two']
enum Enum
member one
if ['defined(IFONE)']
member two
if ['defined(IFCOND)']
object Base
member base1: Enum optional=False
object Variant1
member var1: str optional=False
if ['defined(IFSTR)']
object Variant2
object Object
base Base
tag base1
case one: Variant1
case two: Variant2
if ['IFTWO']
object q_obj_Variant1-wrapper
member data: Variant1 optional=False
object q_obj_Variant2-wrapper
member data: Variant2 optional=False
enum SugaredUnionKind ['one', 'two']
enum SugaredUnionKind
member one
member two
if ['IFTWO']
object SugaredUnion
member type: SugaredUnionKind optional=False
tag type
case one: q_obj_Variant1-wrapper
case two: q_obj_Variant2-wrapper
if ['IFTWO']
object q_obj_cmd-arg
member arg1: int optional=False
member arg2: str optional=True

View File

@ -84,12 +84,12 @@ Examples:
@table @asis
@item @code{one}
The @emph{one} @{and only@}
@*@b{If:} @code{defined(IFONE)}
@item @code{two}
Not documented
@end table
@code{two} is undocumented
@b{If:} @code{defined(IFCOND)}
@end deftp
@ -119,6 +119,7 @@ Another paragraph (but no @code{var}: line)
@table @asis
@item @code{var1: string}
Not documented
@*@b{If:} @code{defined(IFSTR)}
@end table
@end deftp
@ -141,7 +142,7 @@ Not documented
@table @asis
@item The members of @code{Base}
@item The members of @code{Variant1} when @code{base1} is @t{"one"}
@item The members of @code{Variant2} when @code{base1} is @t{"two"}
@item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO})
@end table
@end deftp
@ -157,7 +158,7 @@ Not documented
@item @code{type}
One of @t{"one"}, @t{"two"}
@item @code{data: Variant1} when @code{type} is @t{"one"}
@item @code{data: Variant2} when @code{type} is @t{"two"}
@item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO})
@end table
@end deftp

View File

@ -1 +1,2 @@
tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
Valid keys are 'base', 'data', 'if', 'struct'.

View File

@ -1,3 +1,10 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool

View File

@ -0,0 +1 @@
tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
# we reject any enum member that is not a string
{ 'enum': 'MyEnum', 'data': [ [ ] ] }

View File

View File

@ -0,0 +1,2 @@
tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum'
Valid keys are 'if', 'name'.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
# we reject any enum member that is not a string or a dict with 'name'
{ 'enum': 'MyEnum', 'data': [ { 'name': 'foo', 'bad-key': 'str' } ] }

View File

@ -1 +0,0 @@
tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name

View File

@ -1,2 +0,0 @@
# we reject any enum member that is not a string
{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }

View File

@ -0,0 +1 @@
tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# check invalid 'if' type
{ 'enum': 'TestIfEnum', 'data':
[ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] }

View File

View File

@ -1,6 +1,13 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module event-case.json
event oops None
boxed=False

View File

@ -0,0 +1 @@
tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
{ 'event': 'EVENT_A',
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }

View File

@ -1,2 +1,2 @@
{ 'event': 'EVENT_A',
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } }

View File

@ -0,0 +1 @@
tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,11 @@
# we require branches to be a struct name
# TODO: should we allow anonymous inline branch types?
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'struct': 'Base',
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
{ 'union': 'TestUnion',
'base': 'Base',
'discriminator': 'enum1',
'data': { 'value1': { 'string': 'str' },
'value2': { 'integer': 'int' } } }

View File

@ -7,5 +7,5 @@
{ 'union': 'TestUnion',
'base': 'Base',
'discriminator': 'enum1',
'data': { 'value1': { 'string': 'str' },
'data': { 'value1': { 'type': {} },
'value2': { 'integer': 'int' } } }

View File

@ -0,0 +1 @@
tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,17 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
{ 'struct': 'TestBase',
'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } }
{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': 'TestBase',
'discriminator': 'enum1',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }

View File

@ -1,6 +1,13 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module ident-with-escape.json
object q_obj_fooA-arg
member bar1: str optional=False

View File

@ -1,9 +1,19 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module include-relpath.json
include include/relpath.json
module include/relpath.json
include include-relpath-sub.json
module include-relpath-sub.json
enum Status ['good', 'bad', 'ugly']
enum Status
member good
member bad
member ugly

View File

@ -1,10 +1,20 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module include-repetition.json
include comments.json
module comments.json
enum Status ['good', 'bad', 'ugly']
enum Status
member good
member bad
member ugly
module include-repetition.json
include include-repetition-sub.json
module include-repetition-sub.json

View File

@ -1,7 +1,17 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module include-simple.json
include include-simple-sub.json
module include-simple-sub.json
enum Status ['good', 'bad', 'ugly']
enum Status
member good
member bad
member ugly

View File

@ -1,6 +1,13 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module indented-expr.json
command eins None -> None
gen=True success_response=True boxed=False oob=False preconfig=False

View File

@ -0,0 +1 @@
tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# inline subtypes collide with our desired future use of defaults
{ 'command': 'foo',
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }

View File

@ -1,3 +1,3 @@
# inline subtypes collide with our desired future use of defaults
{ 'command': 'foo',
'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
'data': { 'a' : { 'type': {} }, 'b' : 'str' } }

View File

@ -11,7 +11,7 @@
'guest-sync' ] } }
{ 'struct': 'TestStruct',
'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } }
# for testing enums
{ 'struct': 'NestedEnumsOne',
@ -77,7 +77,7 @@
{ 'union': 'UserDefFlatUnion',
'base': 'UserDefUnionBase', # intentional forward reference
'discriminator': 'enum1',
'data': { 'value1' : 'UserDefA',
'data': { 'value1' : {'type': 'UserDefA'},
'value2' : 'UserDefB',
'value3' : 'UserDefB'
# 'value4' defaults to empty
@ -98,7 +98,7 @@
{ 'struct': 'WrapAlternate',
'data': { 'alt': 'UserDefAlternate' } }
{ 'alternate': 'UserDefAlternate',
'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int',
'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int',
'n': 'null' } }
{ 'struct': 'UserDefC',
@ -134,7 +134,7 @@
{ 'command': 'user_def_cmd', 'data': {} }
{ 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
{ 'command': 'user_def_cmd2',
'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'},
'returns': 'UserDefTwo' }
{ 'command': 'cmd-success-response', 'data': {}, 'success-response': false }
@ -166,7 +166,7 @@
# testing event
{ 'struct': 'EventStructOne',
'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } }
{ 'event': 'EVENT_A' }
{ 'event': 'EVENT_B',
@ -201,23 +201,40 @@
# test 'if' condition handling
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
{ 'struct': 'TestIfStruct', 'data':
{ 'foo': 'int',
'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} },
'if': 'defined(TEST_IF_STRUCT)' }
{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
{ 'enum': 'TestIfEnum', 'data':
[ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
'if': 'defined(TEST_IF_ENUM)' }
{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
{ 'union': 'TestIfUnion', 'data':
{ 'foo': 'TestStruct',
'union_bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
{ 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' },
'if': 'defined(TEST_IF_UNION)' }
{ 'alternate': 'TestIfAlternate', 'data':
{ 'foo': 'int',
'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
{ 'command': 'TestIfAlternateCmd', 'data': { 'alt_cmd_arg': 'TestIfAlternate' },
'if': 'defined(TEST_IF_ALT)' }
{ 'command': 'TestIfCmd', 'data':
{ 'foo': 'TestIfStruct',
'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } },
'returns': 'UserDefThree',
'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' }
{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
{ 'event': 'TestIfEvent', 'data':
{ 'foo': 'TestIfStruct',
'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_EVT_BAR)' } },
'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }

View File

@ -1,6 +1,13 @@
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
enum QType
prefix QTYPE
member none
member qnull
member qnum
member qstring
member qdict
member qlist
member qbool
module qapi-schema-test.json
object TestStruct
member integer: int optional=False
@ -11,19 +18,25 @@ object NestedEnumsOne
member enum2: EnumOne optional=True
member enum3: EnumOne optional=False
member enum4: EnumOne optional=True
enum MyEnum []
enum MyEnum
object Empty1
object Empty2
base Empty1
command user_def_cmd0 Empty2 -> Empty2
gen=True success_response=True boxed=False oob=False preconfig=False
enum QEnumTwo ['value1', 'value2']
enum QEnumTwo
prefix QENUM_TWO
member value1
member value2
object UserDefOne
base UserDefZero
member string: str optional=False
member enum1: EnumOne optional=True
enum EnumOne ['value1', 'value2', 'value3', 'value4']
enum EnumOne
member value1
member value2
member value3
member value4
object UserDefZero
member integer: int optional=False
object UserDefTwoDictDict
@ -127,7 +140,21 @@ object q_obj_sizeList-wrapper
member data: sizeList optional=False
object q_obj_anyList-wrapper
member data: anyList optional=False
enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any']
enum UserDefNativeListUnionKind
member integer
member s8
member s16
member s32
member s64
member u8
member u16
member u32
member u64
member number
member boolean
member string
member sizes
member any
object UserDefNativeListUnion
member type: UserDefNativeListUnionKind optional=False
tag type
@ -204,7 +231,8 @@ event EVENT_E UserDefZero
boxed=True
event EVENT_F UserDefAlternate
boxed=True
enum __org.qemu_x-Enum ['__org.qemu_x-value']
enum __org.qemu_x-Enum
member __org.qemu_x-value
object __org.qemu_x-Base
member __org.qemu_x-member1: __org.qemu_x-Enum optional=False
object __org.qemu_x-Struct
@ -213,7 +241,8 @@ object __org.qemu_x-Struct
member wchar-t: int optional=True
object q_obj_str-wrapper
member data: str optional=False
enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
enum __org.qemu_x-Union1Kind
member __org.qemu_x-branch
object __org.qemu_x-Union1
member type: __org.qemu_x-Union1Kind optional=False
tag type
@ -239,25 +268,50 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio
gen=True success_response=True boxed=False oob=False preconfig=False
object TestIfStruct
member foo: int optional=False
member bar: int optional=False
if ['defined(TEST_IF_STRUCT_BAR)']
if ['defined(TEST_IF_STRUCT)']
enum TestIfEnum ['foo', 'bar']
enum TestIfEnum
member foo
member bar
if ['defined(TEST_IF_ENUM_BAR)']
if ['defined(TEST_IF_ENUM)']
object q_obj_TestStruct-wrapper
member data: TestStruct optional=False
enum TestIfUnionKind ['foo']
enum TestIfUnionKind
member foo
member union_bar
if ['defined(TEST_IF_UNION_BAR)']
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
object TestIfUnion
member type: TestIfUnionKind optional=False
tag type
case foo: q_obj_TestStruct-wrapper
case union_bar: q_obj_str-wrapper
if ['defined(TEST_IF_UNION_BAR)']
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
object q_obj_TestIfUnionCmd-arg
member union_cmd_arg: TestIfUnion optional=False
if ['defined(TEST_IF_UNION)']
command TestIfUnionCmd q_obj_TestIfUnionCmd-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
if ['defined(TEST_IF_UNION)']
alternate TestIfAlternate
tag type
case foo: int
case bar: TestStruct
if ['defined(TEST_IF_ALT_BAR)']
if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
object q_obj_TestIfAlternateCmd-arg
member alt_cmd_arg: TestIfAlternate optional=False
if ['defined(TEST_IF_ALT)']
command TestIfAlternateCmd q_obj_TestIfAlternateCmd-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
if ['defined(TEST_IF_ALT)']
object q_obj_TestIfCmd-arg
member foo: TestIfStruct optional=False
member bar: TestIfEnum optional=False
if ['defined(TEST_IF_CMD_BAR)']
if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
@ -266,6 +320,8 @@ command TestCmdReturnDefThree None -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
object q_obj_TestIfEvent-arg
member foo: TestIfStruct optional=False
member bar: TestIfEnum optional=False
if ['defined(TEST_IF_EVT_BAR)']
if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
event TestIfEvent q_obj_TestIfEvent-arg
boxed=False

View File

@ -0,0 +1 @@
tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# Long form of member must have a value member 'type'
{ 'struct': 'foo',
'data': { '*a': { 'case': 'foo' } } }

View File

@ -23,10 +23,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
def visit_include(self, name, info):
print('include %s' % name)
def visit_enum_type(self, name, info, ifcond, values, prefix):
print('enum %s %s' % (name, values))
def visit_enum_type(self, name, info, ifcond, members, prefix):
print('enum %s' % name)
if prefix:
print(' prefix %s' % prefix)
for m in members:
print(' member %s' % m.name)
self._print_if(m.ifcond, indent=8)
self._print_if(ifcond)
def visit_object_type(self, name, info, ifcond, base, members, variants):
@ -36,6 +39,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
for m in members:
print(' member %s: %s optional=%s'
% (m.name, m.type.name, m.optional))
self._print_if(m.ifcond, 8)
self._print_variants(variants)
self._print_if(ifcond)
@ -64,6 +68,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
print(' tag %s' % variants.tag_member.name)
for v in variants.variants:
print(' case %s: %s' % (v.name, v.type.name))
QAPISchemaTestVisitor._print_if(v.ifcond, indent=8)
@staticmethod
def _print_if(ifcond, indent=4):

View File

@ -0,0 +1 @@
tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
# Long form of member must have a value member 'type'
{ 'union': 'UnionInvalidBranch',
'data': { 'integer': { 'if': 'foo'},
's8': 'int8' } }

View File

@ -1 +1,2 @@
tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar'
tests/qapi-schema/unknown-expr-key.json:2: Unknown keys 'bogus', 'phony' in struct 'bar'
Valid keys are 'base', 'data', 'if', 'struct'.

View File

@ -1,2 +1,2 @@
# we reject an expression with unknown top-level keys
{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } }
{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { }, 'phony': { } }

View File

@ -69,7 +69,6 @@ echo
_send_qemu_cmd $QEMU_HANDLE \
"{'execute': 'drive-mirror',
'arguments': {'device': 'testdisk',
'mode': 'absolute-paths',
'format': '$IMGFMT',
'target': '$DEST_IMG',
'sync': 'full',

View File

@ -1950,7 +1950,7 @@ static void test_qemu_strtou64_full_max(void)
static void test_qemu_strtosz_simple(void)
{
const char *str;
char *endptr = NULL;
const char *endptr;
int err;
uint64_t res = 0xbaadf00d;
@ -2017,7 +2017,7 @@ static void test_qemu_strtosz_units(void)
const char *p = "1P";
const char *e = "1E";
int err;
char *endptr = NULL;
const char *endptr;
uint64_t res = 0xbaadf00d;
/* default is M */
@ -2066,7 +2066,7 @@ static void test_qemu_strtosz_float(void)
{
const char *str = "12.345M";
int err;
char *endptr = NULL;
const char *endptr;
uint64_t res = 0xbaadf00d;
err = qemu_strtosz(str, &endptr, &res);
@ -2078,7 +2078,7 @@ static void test_qemu_strtosz_float(void)
static void test_qemu_strtosz_invalid(void)
{
const char *str;
char *endptr = NULL;
const char *endptr;
int err;
uint64_t res = 0xbaadf00d;
@ -2096,12 +2096,22 @@ static void test_qemu_strtosz_invalid(void)
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -EINVAL);
g_assert(endptr == str);
str = "inf";
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -EINVAL);
g_assert(endptr == str);
str = "NaN";
err = qemu_strtosz(str, &endptr, &res);
g_assert_cmpint(err, ==, -EINVAL);
g_assert(endptr == str);
}
static void test_qemu_strtosz_trailing(void)
{
const char *str;
char *endptr = NULL;
const char *endptr;
int err;
uint64_t res = 0xbaadf00d;
@ -2126,7 +2136,7 @@ static void test_qemu_strtosz_trailing(void)
static void test_qemu_strtosz_erange(void)
{
const char *str;
char *endptr = NULL;
const char *endptr;
int err;
uint64_t res = 0xbaadf00d;
@ -2160,7 +2170,7 @@ static void test_qemu_strtosz_metric(void)
{
const char *str = "12345k";
int err;
char *endptr = NULL;
const char *endptr;
uint64_t res = 0xbaadf00d;
err = qemu_strtosz_metric(str, &endptr, &res);

View File

@ -92,16 +92,6 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n)
uint64List *tail;
int i;
/* BUG: unsigned numbers above INT64_MAX don't work */
for (i = 0; i < n; i++) {
if (expected[i] > INT64_MAX) {
Error *err = NULL;
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
return;
}
}
visit_type_uint64List(v, NULL, &res, &error_abort);
tail = res;
for (i = 0; i < n; i++) {
@ -117,14 +107,14 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n)
static void test_visitor_in_intList(TestInputVisitorData *data,
const void *unused)
{
/* Note: the visitor *sorts* ranges *unsigned* */
int64_t expect1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20 };
int64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7,
8, 9, 1, 2, 3, 4, 5, 6, 7, 8 };
int64_t expect2[] = { 32767, -32768, -32767 };
int64_t expect3[] = { INT64_MAX, INT64_MIN };
uint64_t expect4[] = { UINT64_MAX };
int64_t expect3[] = { INT64_MIN, INT64_MAX };
int64_t expect4[] = { 1 };
int64_t expect5[] = { INT64_MAX - 2, INT64_MAX - 1, INT64_MAX };
Error *err = NULL;
int64List *res = NULL;
int64List *tail;
Visitor *v;
int64_t val;
@ -140,8 +130,45 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
"-9223372036854775808,9223372036854775807");
check_ilist(v, expect3, ARRAY_SIZE(expect3));
v = visitor_input_test_init(data, "18446744073709551615");
check_ulist(v, expect4, ARRAY_SIZE(expect4));
v = visitor_input_test_init(data, "1-1");
check_ilist(v, expect4, ARRAY_SIZE(expect4));
v = visitor_input_test_init(data,
"9223372036854775805-9223372036854775807");
check_ilist(v, expect5, ARRAY_SIZE(expect5));
/* Value too large */
v = visitor_input_test_init(data, "9223372036854775808");
visit_type_int64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Value too small */
v = visitor_input_test_init(data, "-9223372036854775809");
visit_type_int64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Range not ascending */
v = visitor_input_test_init(data, "3-1");
visit_type_int64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
v = visitor_input_test_init(data, "9223372036854775807-0");
visit_type_int64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Range too big (65536 is the limit against DOS attacks) */
v = visitor_input_test_init(data, "0-65536");
visit_type_int64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Empty list */
@ -161,39 +188,140 @@ static void test_visitor_in_intList(TestInputVisitorData *data,
v = visitor_input_test_init(data, "0,2-3");
/* Would be simpler if the visitor genuinely supported virtual walks */
visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res),
&error_abort);
tail = res;
visit_type_int64(v, NULL, &tail->value, &error_abort);
g_assert_cmpint(tail->value, ==, 0);
tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res));
g_assert(tail);
visit_type_int64(v, NULL, &tail->value, &error_abort);
g_assert_cmpint(tail->value, ==, 2);
tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res));
g_assert(tail);
visit_start_list(v, NULL, NULL, 0, &error_abort);
visit_type_int64(v, NULL, &val, &error_abort);
g_assert_cmpint(val, ==, 0);
visit_type_int64(v, NULL, &val, &error_abort);
g_assert_cmpint(val, ==, 2);
visit_check_list(v, &err);
error_free_or_abort(&err);
visit_end_list(v, (void **)&res);
qapi_free_int64List(res);
visit_end_list(v, NULL);
/* Visit beyond end of list */
v = visitor_input_test_init(data, "0");
visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res),
&error_abort);
tail = res;
visit_type_int64(v, NULL, &tail->value, &err);
g_assert_cmpint(tail->value, ==, 0);
visit_start_list(v, NULL, NULL, 0, &error_abort);
visit_type_int64(v, NULL, &val, &err);
g_assert_cmpint(val, ==, 1); /* BUG */
visit_check_list(v, &error_abort);
visit_end_list(v, (void **)&res);
g_assert_cmpint(val, ==, 0);
visit_type_int64(v, NULL, &val, &err);
error_free_or_abort(&err);
qapi_free_int64List(res);
visit_check_list(v, &error_abort);
visit_end_list(v, NULL);
}
static void test_visitor_in_uintList(TestInputVisitorData *data,
const void *unused)
{
uint64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7,
8, 9, 1, 2, 3, 4, 5, 6, 7, 8 };
uint64_t expect2[] = { 32767, -32768, -32767 };
uint64_t expect3[] = { INT64_MIN, INT64_MAX };
uint64_t expect4[] = { 1 };
uint64_t expect5[] = { UINT64_MAX };
uint64_t expect6[] = { UINT64_MAX - 2, UINT64_MAX - 1, UINT64_MAX };
Error *err = NULL;
uint64List *res = NULL;
Visitor *v;
uint64_t val;
/* Valid lists */
v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8");
check_ulist(v, expect1, ARRAY_SIZE(expect1));
v = visitor_input_test_init(data, "32767,-32768--32767");
check_ulist(v, expect2, ARRAY_SIZE(expect2));
v = visitor_input_test_init(data,
"-9223372036854775808,9223372036854775807");
check_ulist(v, expect3, ARRAY_SIZE(expect3));
v = visitor_input_test_init(data, "1-1");
check_ulist(v, expect4, ARRAY_SIZE(expect4));
v = visitor_input_test_init(data, "18446744073709551615");
check_ulist(v, expect5, ARRAY_SIZE(expect5));
v = visitor_input_test_init(data,
"18446744073709551613-18446744073709551615");
check_ulist(v, expect6, ARRAY_SIZE(expect6));
/* Value too large */
v = visitor_input_test_init(data, "18446744073709551616");
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Value too small */
v = visitor_input_test_init(data, "-18446744073709551616");
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Range not ascending */
v = visitor_input_test_init(data, "3-1");
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
v = visitor_input_test_init(data, "18446744073709551615-0");
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Range too big (65536 is the limit against DOS attacks) */
v = visitor_input_test_init(data, "0-65536");
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Empty list */
v = visitor_input_test_init(data, "");
visit_type_uint64List(v, NULL, &res, &error_abort);
g_assert(!res);
/* Not a list */
v = visitor_input_test_init(data, "not an uint list");
visit_type_uint64List(v, NULL, &res, &err);
error_free_or_abort(&err);
g_assert(!res);
/* Unvisited list tail */
v = visitor_input_test_init(data, "0,2-3");
visit_start_list(v, NULL, NULL, 0, &error_abort);
visit_type_uint64(v, NULL, &val, &error_abort);
g_assert_cmpuint(val, ==, 0);
visit_type_uint64(v, NULL, &val, &error_abort);
g_assert_cmpuint(val, ==, 2);
visit_check_list(v, &err);
error_free_or_abort(&err);
visit_end_list(v, NULL);
/* Visit beyond end of list */
v = visitor_input_test_init(data, "0");
visit_start_list(v, NULL, NULL, 0, &error_abort);
visit_type_uint64(v, NULL, &val, &err);
g_assert_cmpuint(val, ==, 0);
visit_type_uint64(v, NULL, &val, &err);
error_free_or_abort(&err);
visit_check_list(v, &error_abort);
visit_end_list(v, NULL);
}
static void test_visitor_in_bool(TestInputVisitorData *data,
@ -252,6 +380,19 @@ static void test_visitor_in_number(TestInputVisitorData *data,
visit_type_number(v, NULL, &res, &err);
g_assert(!err);
g_assert_cmpfloat(res, ==, value);
/* NaN and infinity has to be rejected */
v = visitor_input_test_init(data, "NaN");
visit_type_number(v, NULL, &res, &err);
error_free_or_abort(&err);
v = visitor_input_test_init(data, "inf");
visit_type_number(v, NULL, &res, &err);
error_free_or_abort(&err);
}
static void test_visitor_in_string(TestInputVisitorData *data,
@ -356,6 +497,8 @@ int main(int argc, char **argv)
&in_visitor_data, test_visitor_in_int);
input_visitor_test_add("/string-visitor/input/intList",
&in_visitor_data, test_visitor_in_intList);
input_visitor_test_add("/string-visitor/input/uintList",
&in_visitor_data, test_visitor_in_uintList);
input_visitor_test_add("/string-visitor/input/bool",
&in_visitor_data, test_visitor_in_bool);
input_visitor_test_add("/string-visitor/input/number",

View File

@ -203,23 +203,21 @@ static int64_t suffix_mul(char suffix, int64_t unit)
/*
* Convert string to bytes, allowing either B/b for bytes, K/k for KB,
* M/m for MB, G/g for GB or T/t for TB. End pointer will be returned
* in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on
* in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on
* other error.
*/
static int do_strtosz(const char *nptr, char **end,
static int do_strtosz(const char *nptr, const char **end,
const char default_suffix, int64_t unit,
uint64_t *result)
{
int retval;
char *endptr;
const char *endptr;
unsigned char c;
int mul_required = 0;
double val, mul, integral, fraction;
errno = 0;
val = strtod(nptr, &endptr);
if (isnan(val) || endptr == nptr || errno != 0) {
retval = -EINVAL;
retval = qemu_strtod_finite(nptr, &endptr, &val);
if (retval) {
goto out;
}
fraction = modf(val, &integral);
@ -259,17 +257,17 @@ out:
return retval;
}
int qemu_strtosz(const char *nptr, char **end, uint64_t *result)
int qemu_strtosz(const char *nptr, const char **end, uint64_t *result)
{
return do_strtosz(nptr, end, 'B', 1024, result);
}
int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result)
int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result)
{
return do_strtosz(nptr, end, 'M', 1024, result);
}
int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result)
int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result)
{
return do_strtosz(nptr, end, 'B', 1000, result);
}
@ -551,6 +549,71 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base,
return check_strtox_error(nptr, ep, endptr, errno);
}
/**
* Convert string @nptr to a double.
*
* This is a wrapper around strtod() that is harder to misuse.
* Semantics of @nptr and @endptr match strtod() with differences
* noted below.
*
* @nptr may be null, and no conversion is performed then.
*
* If no conversion is performed, store @nptr in *@endptr and return
* -EINVAL.
*
* If @endptr is null, and the string isn't fully converted, return
* -EINVAL. This is the case when the pointer that would be stored in
* a non-null @endptr points to a character other than '\0'.
*
* If the conversion overflows, store +/-HUGE_VAL in @result, depending
* on the sign, and return -ERANGE.
*
* If the conversion underflows, store +/-0.0 in @result, depending on the
* sign, and return -ERANGE.
*
* Else store the converted value in @result, and return zero.
*/
int qemu_strtod(const char *nptr, const char **endptr, double *result)
{
char *ep;
if (!nptr) {
if (endptr) {
*endptr = nptr;
}
return -EINVAL;
}
errno = 0;
*result = strtod(nptr, &ep);
return check_strtox_error(nptr, ep, endptr, errno);
}
/**
* Convert string @nptr to a finite double.
*
* Works like qemu_strtod(), except that "NaN" and "inf" are rejected
* with -EINVAL and no conversion is performed.
*/
int qemu_strtod_finite(const char *nptr, const char **endptr, double *result)
{
double tmp;
int ret;
ret = qemu_strtod(nptr, endptr, &tmp);
if (!ret && !isfinite(tmp)) {
if (endptr) {
*endptr = nptr;
}
ret = -EINVAL;
}
if (ret != -EINVAL) {
*result = tmp;
}
return ret;
}
/**
* Searches for the first occurrence of 'c' in 's', and returns a pointer
* to the trailing null byte if none was found.