mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/armbru/tags/pull-qmp-2015-05-05' into staging
drop qapi nested structs # gpg: Signature made Tue May 5 17:40:40 2015 BST using RSA key ID EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" * remotes/armbru/tags/pull-qmp-2015-05-05: (40 commits) qapi: Check for member name conflicts with a base class qapi: Support (subset of) \u escapes in strings qapi: Tweak doc references to QMP when QGA is also meant qapi: Drop dead visitor code related to nested structs qapi: Drop support for inline nested types qapi: Drop inline nested structs in query-pci qapi: Drop inline nested struct in query-version qapi: Drop tests for inline nested structs qapi: Merge UserDefTwo and UserDefNested in tests qapi: Forbid 'type' in schema qapi: Use 'struct' instead of 'type' in schema qapi: Document 'struct' metatype qapi: Prefer 'struct' over 'type' in generator qapi: More rigorous checking for type safety bypass qapi: Whitelist commands that don't return dictionary qapi: Require valid names qapi: More rigourous checking of types qapi: Add some type check tests qapi: Unify type bypass and add tests qapi: Allow true, false and null in schema json ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+373
-108
File diff suppressed because it is too large
Load Diff
+89
-26
@@ -1,10 +1,21 @@
|
||||
QEMU Machine Protocol Specification
|
||||
|
||||
0. About This Document
|
||||
======================
|
||||
|
||||
Copyright (C) 2009-2015 Red Hat, Inc.
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
later. See the COPYING file in the top-level directory.
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
|
||||
This document specifies the QEMU Machine Protocol (QMP), a JSON-based protocol
|
||||
which is available for applications to operate QEMU at the machine-level.
|
||||
This document specifies the QEMU Machine Protocol (QMP), a JSON-based
|
||||
protocol which is available for applications to operate QEMU at the
|
||||
machine-level. It is also in use by the QEMU Guest Agent (QGA), which
|
||||
is available for host applications to interact with the guest
|
||||
operating system.
|
||||
|
||||
2. Protocol Specification
|
||||
=========================
|
||||
@@ -18,14 +29,27 @@ following format:
|
||||
|
||||
json-DATA-STRUCTURE-NAME
|
||||
|
||||
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
|
||||
the JSON standard:
|
||||
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined
|
||||
by the JSON standard:
|
||||
|
||||
http://www.ietf.org/rfc/rfc4627.txt
|
||||
http://www.ietf.org/rfc/rfc7159.txt
|
||||
|
||||
For convenience, json-object members and json-array elements mentioned in
|
||||
this document will be in a certain order. However, in real protocol usage
|
||||
they can be in ANY order, thus no particular order should be assumed.
|
||||
The protocol is always encoded in UTF-8 except for synchronization
|
||||
bytes (documented below); although thanks to json-string escape
|
||||
sequences, the server will reply using only the strict ASCII subset.
|
||||
|
||||
For convenience, json-object members mentioned in this document will
|
||||
be in a certain order. However, in real protocol usage they can be in
|
||||
ANY order, thus no particular order should be assumed. On the other
|
||||
hand, use of json-array elements presumes that preserving order is
|
||||
important unless specifically documented otherwise. Repeating a key
|
||||
within a json-object gives unpredictable results.
|
||||
|
||||
Also for convenience, the server will accept an extension of
|
||||
'single-quoted' strings in place of the usual "double-quoted"
|
||||
json-string, and both input forms of strings understand an additional
|
||||
escape sequence of "\'" for a single quote. The server will only use
|
||||
double quoting on output.
|
||||
|
||||
2.1 General Definitions
|
||||
-----------------------
|
||||
@@ -52,7 +76,16 @@ The greeting message format is:
|
||||
- The "version" member contains the Server's version information (the format
|
||||
is the same of the query-version command)
|
||||
- The "capabilities" member specify the availability of features beyond the
|
||||
baseline specification
|
||||
baseline specification; the order of elements in this array has no
|
||||
particular significance, so a client must search the entire array
|
||||
when looking for a particular capability
|
||||
|
||||
2.2.1 Capabilities
|
||||
------------------
|
||||
|
||||
As of the date this document was last revised, no server or client
|
||||
capability strings have been defined.
|
||||
|
||||
|
||||
2.3 Issuing Commands
|
||||
--------------------
|
||||
@@ -65,10 +98,14 @@ The format for command execution is:
|
||||
|
||||
- The "execute" member identifies the command to be executed by the Server
|
||||
- The "arguments" member is used to pass any arguments required for the
|
||||
execution of the command, it is optional when no arguments are required
|
||||
execution of the command, it is optional when no arguments are
|
||||
required. Each command documents what contents will be considered
|
||||
valid when handling the json-argument
|
||||
- The "id" member is a transaction identification associated with the
|
||||
command execution, it is optional and will be part of the response if
|
||||
provided
|
||||
provided. The "id" member can be any json-value, although most
|
||||
clients merely use a json-number incremented for each successive
|
||||
command
|
||||
|
||||
2.4 Commands Responses
|
||||
----------------------
|
||||
@@ -81,13 +118,15 @@ of a command execution: success or error.
|
||||
|
||||
The format of a success response is:
|
||||
|
||||
{ "return": json-object, "id": json-value }
|
||||
{ "return": json-value, "id": json-value }
|
||||
|
||||
Where,
|
||||
|
||||
- The "return" member contains the command returned data, which is defined
|
||||
in a per-command basis or an empty json-object if the command does not
|
||||
return data
|
||||
- The "return" member contains the data returned by the command, which
|
||||
is defined on a per-command basis (usually a json-object or
|
||||
json-array of json-objects, but sometimes a json-number, json-string,
|
||||
or json-array of json-strings); it is an empty json-object if the
|
||||
command does not return data
|
||||
- The "id" member contains the transaction identification associated
|
||||
with the command execution if issued by the Client
|
||||
|
||||
@@ -114,7 +153,8 @@ if provided by the client.
|
||||
-----------------------
|
||||
|
||||
As a result of state changes, the Server may send messages unilaterally
|
||||
to the Client at any time. They are called "asynchronous events".
|
||||
to the Client at any time, when not in the middle of any other
|
||||
response. They are called "asynchronous events".
|
||||
|
||||
The format of asynchronous events is:
|
||||
|
||||
@@ -126,13 +166,27 @@ The format of asynchronous events is:
|
||||
- The "event" member contains the event's name
|
||||
- The "data" member contains event specific data, which is defined in a
|
||||
per-event basis, it is optional
|
||||
- The "timestamp" member contains the exact time of when the event occurred
|
||||
in the Server. It is a fixed json-object with time in seconds and
|
||||
microseconds
|
||||
- The "timestamp" member contains the exact time of when the event
|
||||
occurred in the Server. It is a fixed json-object with time in
|
||||
seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if
|
||||
there is a failure to retrieve host time, both members of the
|
||||
timestamp will be set to -1.
|
||||
|
||||
For a listing of supported asynchronous events, please, refer to the
|
||||
qmp-events.txt file.
|
||||
|
||||
2.5 QGA Synchronization
|
||||
-----------------------
|
||||
|
||||
When using QGA, an additional synchronization feature is built into
|
||||
the protocol. If the Client sends a raw 0xFF sentinel byte (not valid
|
||||
JSON), then the Server will reset its state and discard all pending
|
||||
data prior to the sentinel. Conversely, if the Client makes use of
|
||||
the 'guest-sync-delimited' command, the Server will send a raw 0xFF
|
||||
sentinel byte prior to its response, to aid the Client in discarding
|
||||
any data prior to the sentinel.
|
||||
|
||||
|
||||
3. QMP Examples
|
||||
===============
|
||||
|
||||
@@ -145,32 +199,37 @@ This section provides some examples of real QMP usage, in all of them
|
||||
S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
|
||||
"package": ""}, "capabilities": []}}
|
||||
|
||||
3.2 Simple 'stop' execution
|
||||
3.2 Client QMP negotiation
|
||||
--------------------------
|
||||
C: { "execute": "qmp_capabilities" }
|
||||
S: { "return": {}}
|
||||
|
||||
3.3 Simple 'stop' execution
|
||||
---------------------------
|
||||
|
||||
C: { "execute": "stop" }
|
||||
S: { "return": {} }
|
||||
|
||||
3.3 KVM information
|
||||
3.4 KVM information
|
||||
-------------------
|
||||
|
||||
C: { "execute": "query-kvm", "id": "example" }
|
||||
S: { "return": { "enabled": true, "present": true }, "id": "example"}
|
||||
|
||||
3.4 Parsing error
|
||||
3.5 Parsing error
|
||||
------------------
|
||||
|
||||
C: { "execute": }
|
||||
S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
|
||||
|
||||
3.5 Powerdown event
|
||||
3.6 Powerdown event
|
||||
-------------------
|
||||
|
||||
S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
|
||||
"event": "POWERDOWN" }
|
||||
|
||||
4. Capabilities Negotiation
|
||||
----------------------------
|
||||
===========================
|
||||
|
||||
When a Client successfully establishes a connection, the Server is in
|
||||
Capabilities Negotiation mode.
|
||||
@@ -189,7 +248,7 @@ effect, all commands (except qmp_capabilities) are allowed and asynchronous
|
||||
messages are delivered.
|
||||
|
||||
5 Compatibility Considerations
|
||||
------------------------------
|
||||
==============================
|
||||
|
||||
All protocol changes or new features which modify the protocol format in an
|
||||
incompatible way are disabled by default and will be advertised by the
|
||||
@@ -213,12 +272,16 @@ However, Clients must not assume any particular:
|
||||
- Amount of errors generated by a command, that is, new errors can be added
|
||||
to any existing command in newer versions of the Server
|
||||
|
||||
Any command or field name beginning with "x-" is deemed experimental,
|
||||
and may be withdrawn or changed in an incompatible manner in a future
|
||||
release.
|
||||
|
||||
Of course, the Server does guarantee to send valid JSON. But apart from
|
||||
this, a Client should be "conservative in what they send, and liberal in
|
||||
what they accept".
|
||||
|
||||
6. Downstream extension of QMP
|
||||
------------------------------
|
||||
==============================
|
||||
|
||||
We recommend that downstream consumers of QEMU do *not* modify QMP.
|
||||
Management tools should be able to support both upstream and downstream
|
||||
|
||||
@@ -60,7 +60,7 @@ void hmp_info_version(Monitor *mon, const QDict *qdict)
|
||||
info = qmp_query_version(NULL);
|
||||
|
||||
monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
|
||||
info->qemu.major, info->qemu.minor, info->qemu.micro,
|
||||
info->qemu->major, info->qemu->minor, info->qemu->micro,
|
||||
info->package);
|
||||
|
||||
qapi_free_VersionInfo(info);
|
||||
@@ -648,14 +648,14 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
|
||||
dev->slot, dev->function);
|
||||
monitor_printf(mon, " ");
|
||||
|
||||
if (dev->class_info.has_desc) {
|
||||
monitor_printf(mon, "%s", dev->class_info.desc);
|
||||
if (dev->class_info->has_desc) {
|
||||
monitor_printf(mon, "%s", dev->class_info->desc);
|
||||
} else {
|
||||
monitor_printf(mon, "Class %04" PRId64, dev->class_info.q_class);
|
||||
monitor_printf(mon, "Class %04" PRId64, dev->class_info->q_class);
|
||||
}
|
||||
|
||||
monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
|
||||
dev->id.vendor, dev->id.device);
|
||||
dev->id->vendor, dev->id->device);
|
||||
|
||||
if (dev->has_irq) {
|
||||
monitor_printf(mon, " IRQ %" PRId64 ".\n", dev->irq);
|
||||
@@ -663,25 +663,25 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
|
||||
|
||||
if (dev->has_pci_bridge) {
|
||||
monitor_printf(mon, " BUS %" PRId64 ".\n",
|
||||
dev->pci_bridge->bus.number);
|
||||
dev->pci_bridge->bus->number);
|
||||
monitor_printf(mon, " secondary bus %" PRId64 ".\n",
|
||||
dev->pci_bridge->bus.secondary);
|
||||
dev->pci_bridge->bus->secondary);
|
||||
monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
|
||||
dev->pci_bridge->bus.subordinate);
|
||||
dev->pci_bridge->bus->subordinate);
|
||||
|
||||
monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
|
||||
dev->pci_bridge->bus.io_range->base,
|
||||
dev->pci_bridge->bus.io_range->limit);
|
||||
dev->pci_bridge->bus->io_range->base,
|
||||
dev->pci_bridge->bus->io_range->limit);
|
||||
|
||||
monitor_printf(mon,
|
||||
" memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
|
||||
dev->pci_bridge->bus.memory_range->base,
|
||||
dev->pci_bridge->bus.memory_range->limit);
|
||||
dev->pci_bridge->bus->memory_range->base,
|
||||
dev->pci_bridge->bus->memory_range->limit);
|
||||
|
||||
monitor_printf(mon, " prefetchable memory range "
|
||||
"[0x%08"PRIx64", 0x%08"PRIx64"]\n",
|
||||
dev->pci_bridge->bus.prefetchable_range->base,
|
||||
dev->pci_bridge->bus.prefetchable_range->limit);
|
||||
dev->pci_bridge->bus->prefetchable_range->base,
|
||||
dev->pci_bridge->bus->prefetchable_range->limit);
|
||||
}
|
||||
|
||||
for (region = dev->regions; region; region = region->next) {
|
||||
|
||||
+23
-19
@@ -1456,24 +1456,26 @@ static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus,
|
||||
int bus_num)
|
||||
{
|
||||
PciBridgeInfo *info;
|
||||
PciMemoryRange *range;
|
||||
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info = g_new0(PciBridgeInfo, 1);
|
||||
|
||||
info->bus.number = dev->config[PCI_PRIMARY_BUS];
|
||||
info->bus.secondary = dev->config[PCI_SECONDARY_BUS];
|
||||
info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS];
|
||||
info->bus = g_new0(PciBusInfo, 1);
|
||||
info->bus->number = dev->config[PCI_PRIMARY_BUS];
|
||||
info->bus->secondary = dev->config[PCI_SECONDARY_BUS];
|
||||
info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS];
|
||||
|
||||
info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range));
|
||||
info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
range = info->bus->io_range = g_new0(PciMemoryRange, 1);
|
||||
range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
|
||||
info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range));
|
||||
info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
range = info->bus->memory_range = g_new0(PciMemoryRange, 1);
|
||||
range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
|
||||
info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range));
|
||||
info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1);
|
||||
range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
|
||||
if (dev->config[PCI_SECONDARY_BUS] != 0) {
|
||||
PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]);
|
||||
@@ -1494,21 +1496,23 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus,
|
||||
uint8_t type;
|
||||
int class;
|
||||
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info = g_new0(PciDeviceInfo, 1);
|
||||
info->bus = bus_num;
|
||||
info->slot = PCI_SLOT(dev->devfn);
|
||||
info->function = PCI_FUNC(dev->devfn);
|
||||
|
||||
info->class_info = g_new0(PciDeviceClass, 1);
|
||||
class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
|
||||
info->class_info.q_class = class;
|
||||
info->class_info->q_class = class;
|
||||
desc = get_class_desc(class);
|
||||
if (desc->desc) {
|
||||
info->class_info.has_desc = true;
|
||||
info->class_info.desc = g_strdup(desc->desc);
|
||||
info->class_info->has_desc = true;
|
||||
info->class_info->desc = g_strdup(desc->desc);
|
||||
}
|
||||
|
||||
info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
|
||||
info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID);
|
||||
info->id = g_new0(PciDeviceId, 1);
|
||||
info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
|
||||
info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID);
|
||||
info->regions = qmp_query_pci_regions(dev);
|
||||
info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : "");
|
||||
|
||||
|
||||
+152
-118
File diff suppressed because it is too large
Load Diff
+35
-37
@@ -26,7 +26,7 @@
|
||||
#
|
||||
##
|
||||
|
||||
{ 'type': 'SnapshotInfo',
|
||||
{ 'struct': 'SnapshotInfo',
|
||||
'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int',
|
||||
'date-sec': 'int', 'date-nsec': 'int',
|
||||
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
|
||||
@@ -45,7 +45,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'ImageInfoSpecificQCow2',
|
||||
{ 'struct': 'ImageInfoSpecificQCow2',
|
||||
'data': {
|
||||
'compat': 'str',
|
||||
'*lazy-refcounts': 'bool',
|
||||
@@ -66,7 +66,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'ImageInfoSpecificVmdk',
|
||||
{ 'struct': 'ImageInfoSpecificVmdk',
|
||||
'data': {
|
||||
'create-type': 'str',
|
||||
'cid': 'int',
|
||||
@@ -126,7 +126,7 @@
|
||||
#
|
||||
##
|
||||
|
||||
{ 'type': 'ImageInfo',
|
||||
{ 'struct': 'ImageInfo',
|
||||
'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool',
|
||||
'*actual-size': 'int', 'virtual-size': 'int',
|
||||
'*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool',
|
||||
@@ -178,7 +178,7 @@
|
||||
#
|
||||
##
|
||||
|
||||
{ 'type': 'ImageCheck',
|
||||
{ 'struct': 'ImageCheck',
|
||||
'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int',
|
||||
'*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int',
|
||||
'*corruptions-fixed': 'int', '*leaks-fixed': 'int',
|
||||
@@ -196,7 +196,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'BlockdevCacheInfo',
|
||||
{ 'struct': 'BlockdevCacheInfo',
|
||||
'data': { 'writeback': 'bool',
|
||||
'direct': 'bool',
|
||||
'no-flush': 'bool' } }
|
||||
@@ -267,7 +267,7 @@
|
||||
# Since: 0.14.0
|
||||
#
|
||||
##
|
||||
{ 'type': 'BlockDeviceInfo',
|
||||
{ 'struct': 'BlockDeviceInfo',
|
||||
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
|
||||
'*backing_file': 'str', 'backing_file_depth': 'int',
|
||||
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
||||
@@ -321,7 +321,7 @@
|
||||
#
|
||||
# Since 1.7
|
||||
##
|
||||
{ 'type': 'BlockDeviceMapEntry',
|
||||
{ 'struct': 'BlockDeviceMapEntry',
|
||||
'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool',
|
||||
'data': 'bool', '*offset': 'int' } }
|
||||
|
||||
@@ -340,7 +340,7 @@
|
||||
#
|
||||
# Since: 1.3
|
||||
##
|
||||
{ 'type': 'BlockDirtyInfo',
|
||||
{ 'struct': 'BlockDirtyInfo',
|
||||
'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32',
|
||||
'frozen': 'bool'} }
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'BlockInfo',
|
||||
{ 'struct': 'BlockInfo',
|
||||
'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
|
||||
'locked': 'bool', '*inserted': 'BlockDeviceInfo',
|
||||
'*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus',
|
||||
@@ -428,7 +428,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'BlockDeviceStats',
|
||||
{ 'struct': 'BlockDeviceStats',
|
||||
'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
|
||||
'wr_operations': 'int', 'flush_operations': 'int',
|
||||
'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
|
||||
@@ -454,7 +454,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'BlockStats',
|
||||
{ 'struct': 'BlockStats',
|
||||
'data': {'*device': 'str', '*node-name': 'str',
|
||||
'stats': 'BlockDeviceStats',
|
||||
'*parent': 'BlockStats',
|
||||
@@ -567,7 +567,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'type': 'BlockJobInfo',
|
||||
{ 'struct': 'BlockJobInfo',
|
||||
'data': {'type': 'str', 'device': 'str', 'len': 'int',
|
||||
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
|
||||
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} }
|
||||
@@ -677,7 +677,7 @@
|
||||
# @mode: #optional whether and how QEMU should create a new image, default is
|
||||
# 'absolute-paths'.
|
||||
##
|
||||
{ 'type': 'BlockdevSnapshot',
|
||||
{ 'struct': 'BlockdevSnapshot',
|
||||
'data': { '*device': 'str', '*node-name': 'str',
|
||||
'snapshot-file': 'str', '*snapshot-node-name': 'str',
|
||||
'*format': 'str', '*mode': 'NewImageMode' } }
|
||||
@@ -721,7 +721,7 @@
|
||||
#
|
||||
# Since: 1.6
|
||||
##
|
||||
{ 'type': 'DriveBackup',
|
||||
{ 'struct': 'DriveBackup',
|
||||
'data': { 'device': 'str', 'target': 'str', '*format': 'str',
|
||||
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
|
||||
'*speed': 'int', '*bitmap': 'str',
|
||||
@@ -756,7 +756,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'BlockdevBackup',
|
||||
{ 'struct': 'BlockdevBackup',
|
||||
'data': { 'device': 'str', 'target': 'str',
|
||||
'sync': 'MirrorSyncMode',
|
||||
'*speed': 'int',
|
||||
@@ -977,7 +977,7 @@
|
||||
#
|
||||
# Since 2.4
|
||||
##
|
||||
{ 'type': 'BlockDirtyBitmap',
|
||||
{ 'struct': 'BlockDirtyBitmap',
|
||||
'data': { 'node': 'str', 'name': 'str' } }
|
||||
|
||||
##
|
||||
@@ -992,7 +992,7 @@
|
||||
#
|
||||
# Since 2.4
|
||||
##
|
||||
{ 'type': 'BlockDirtyBitmapAdd',
|
||||
{ 'struct': 'BlockDirtyBitmapAdd',
|
||||
'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32' } }
|
||||
|
||||
##
|
||||
@@ -1313,7 +1313,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevCacheOptions',
|
||||
{ 'struct': 'BlockdevCacheOptions',
|
||||
'data': { '*writeback': 'bool',
|
||||
'*direct': 'bool',
|
||||
'*no-flush': 'bool' } }
|
||||
@@ -1360,7 +1360,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBase',
|
||||
{ 'struct': 'BlockdevOptionsBase',
|
||||
'data': { 'driver': 'BlockdevDriver',
|
||||
'*id': 'str',
|
||||
'*node-name': 'str',
|
||||
@@ -1382,7 +1382,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsFile',
|
||||
{ 'struct': 'BlockdevOptionsFile',
|
||||
'data': { 'filename': 'str' } }
|
||||
|
||||
##
|
||||
@@ -1397,7 +1397,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsNull',
|
||||
{ 'struct': 'BlockdevOptionsNull',
|
||||
'data': { '*size': 'int', '*latency-ns': 'uint64' } }
|
||||
|
||||
##
|
||||
@@ -1413,7 +1413,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsVVFAT',
|
||||
{ 'struct': 'BlockdevOptionsVVFAT',
|
||||
'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool',
|
||||
'*rw': 'bool' } }
|
||||
|
||||
@@ -1427,7 +1427,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsGenericFormat',
|
||||
{ 'struct': 'BlockdevOptionsGenericFormat',
|
||||
'data': { 'file': 'BlockdevRef' } }
|
||||
|
||||
##
|
||||
@@ -1443,7 +1443,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsGenericCOWFormat',
|
||||
{ 'struct': 'BlockdevOptionsGenericCOWFormat',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*backing': 'BlockdevRef' } }
|
||||
|
||||
@@ -1479,7 +1479,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'Qcow2OverlapCheckFlags',
|
||||
{ 'struct': 'Qcow2OverlapCheckFlags',
|
||||
'data': { '*template': 'Qcow2OverlapCheckMode',
|
||||
'*main-header': 'bool',
|
||||
'*active-l1': 'bool',
|
||||
@@ -1503,8 +1503,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'union': 'Qcow2OverlapChecks',
|
||||
'discriminator': {},
|
||||
{ 'alternate': 'Qcow2OverlapChecks',
|
||||
'data': { 'flags': 'Qcow2OverlapCheckFlags',
|
||||
'mode': 'Qcow2OverlapCheckMode' } }
|
||||
|
||||
@@ -1541,7 +1540,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsQcow2',
|
||||
{ 'struct': 'BlockdevOptionsQcow2',
|
||||
'base': 'BlockdevOptionsGenericCOWFormat',
|
||||
'data': { '*lazy-refcounts': 'bool',
|
||||
'*pass-discard-request': 'bool',
|
||||
@@ -1576,7 +1575,7 @@
|
||||
# use the default value, 'archipelago'.
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsArchipelago',
|
||||
{ 'struct': 'BlockdevOptionsArchipelago',
|
||||
'data': { 'volume': 'str',
|
||||
'*mport': 'int',
|
||||
'*vport': 'int',
|
||||
@@ -1628,7 +1627,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlkdebugInjectErrorOptions',
|
||||
{ 'struct': 'BlkdebugInjectErrorOptions',
|
||||
'data': { 'event': 'BlkdebugEvent',
|
||||
'*state': 'int',
|
||||
'*errno': 'int',
|
||||
@@ -1651,7 +1650,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlkdebugSetStateOptions',
|
||||
{ 'struct': 'BlkdebugSetStateOptions',
|
||||
'data': { 'event': 'BlkdebugEvent',
|
||||
'*state': 'int',
|
||||
'new_state': 'int' } }
|
||||
@@ -1673,7 +1672,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBlkdebug',
|
||||
{ 'struct': 'BlockdevOptionsBlkdebug',
|
||||
'data': { 'image': 'BlockdevRef',
|
||||
'*config': 'str',
|
||||
'*align': 'int',
|
||||
@@ -1691,7 +1690,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBlkverify',
|
||||
{ 'struct': 'BlockdevOptionsBlkverify',
|
||||
'data': { 'test': 'BlockdevRef',
|
||||
'raw': 'BlockdevRef' } }
|
||||
|
||||
@@ -1728,7 +1727,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsQuorum',
|
||||
{ 'struct': 'BlockdevOptionsQuorum',
|
||||
'data': { '*blkverify': 'bool',
|
||||
'children': [ 'BlockdevRef' ],
|
||||
'vote-threshold': 'int',
|
||||
@@ -1795,8 +1794,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'union': 'BlockdevRef',
|
||||
'discriminator': {},
|
||||
{ 'alternate': 'BlockdevRef',
|
||||
'data': { 'definition': 'BlockdevOptions',
|
||||
'reference': 'str' } }
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevSnapshotInternal',
|
||||
{ 'struct': 'BlockdevSnapshotInternal',
|
||||
'data': { 'device': 'str', 'name': 'str' } }
|
||||
|
||||
##
|
||||
|
||||
+21
-9
@@ -28,16 +28,29 @@
|
||||
'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
|
||||
'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
|
||||
|
||||
##
|
||||
# @VersionTriple
|
||||
#
|
||||
# A three-part version number.
|
||||
#
|
||||
# @qemu.major: The major version number.
|
||||
#
|
||||
# @qemu.minor: The minor version number.
|
||||
#
|
||||
# @qemu.micro: The micro version number.
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'VersionTriple',
|
||||
'data': {'major': 'int', 'minor': 'int', 'micro': 'int'} }
|
||||
|
||||
|
||||
##
|
||||
# @VersionInfo:
|
||||
#
|
||||
# A description of QEMU's version.
|
||||
#
|
||||
# @qemu.major: The major version of QEMU
|
||||
#
|
||||
# @qemu.minor: The minor version of QEMU
|
||||
#
|
||||
# @qemu.micro: The micro version of QEMU. By current convention, a micro
|
||||
# @qemu: The version of QEMU. By current convention, a micro
|
||||
# version of 50 signifies a development branch. A micro version
|
||||
# greater than or equal to 90 signifies a release candidate for
|
||||
# the next minor version. A micro version of less than 50
|
||||
@@ -50,9 +63,8 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'VersionInfo',
|
||||
'data': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
|
||||
'package': 'str'} }
|
||||
{ 'struct': 'VersionInfo',
|
||||
'data': {'qemu': 'VersionTriple', 'package': 'str'} }
|
||||
|
||||
##
|
||||
# @query-version:
|
||||
@@ -74,7 +86,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'CommandInfo', 'data': {'name': 'str'} }
|
||||
{ 'struct': 'CommandInfo', 'data': {'name': 'str'} }
|
||||
|
||||
##
|
||||
# @query-commands:
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
#
|
||||
# Since 2.2
|
||||
##
|
||||
{ 'type': 'TraceEventInfo',
|
||||
{ 'struct': 'TraceEventInfo',
|
||||
'data': {'name': 'str', 'state': 'TraceEventState'} }
|
||||
|
||||
##
|
||||
|
||||
+18
-18
@@ -150,7 +150,7 @@
|
||||
#
|
||||
# Since 1.1.0
|
||||
##
|
||||
{ 'type': 'GuestAgentCommandInfo',
|
||||
{ 'struct': 'GuestAgentCommandInfo',
|
||||
'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
|
||||
|
||||
##
|
||||
@@ -164,7 +164,7 @@
|
||||
#
|
||||
# Since 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestAgentInfo',
|
||||
{ 'struct': 'GuestAgentInfo',
|
||||
'data': { 'version': 'str',
|
||||
'supported_commands': ['GuestAgentCommandInfo'] } }
|
||||
##
|
||||
@@ -195,7 +195,7 @@
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
|
||||
'success-response': 'no' }
|
||||
'success-response': false }
|
||||
|
||||
##
|
||||
# @guest-file-open:
|
||||
@@ -242,7 +242,7 @@
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileRead',
|
||||
{ 'struct': 'GuestFileRead',
|
||||
'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
|
||||
|
||||
##
|
||||
@@ -274,7 +274,7 @@
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileWrite',
|
||||
{ 'struct': 'GuestFileWrite',
|
||||
'data': { 'count': 'int', 'eof': 'bool' } }
|
||||
|
||||
##
|
||||
@@ -309,7 +309,7 @@
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileSeek',
|
||||
{ 'struct': 'GuestFileSeek',
|
||||
'data': { 'position': 'int', 'eof': 'bool' } }
|
||||
|
||||
##
|
||||
@@ -470,7 +470,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-disk', 'success-response': 'no' }
|
||||
{ 'command': 'guest-suspend-disk', 'success-response': false }
|
||||
|
||||
##
|
||||
# @guest-suspend-ram
|
||||
@@ -502,7 +502,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-ram', 'success-response': 'no' }
|
||||
{ 'command': 'guest-suspend-ram', 'success-response': false }
|
||||
|
||||
##
|
||||
# @guest-suspend-hybrid
|
||||
@@ -529,7 +529,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-hybrid', 'success-response': 'no' }
|
||||
{ 'command': 'guest-suspend-hybrid', 'success-response': false }
|
||||
|
||||
##
|
||||
# @GuestIpAddressType:
|
||||
@@ -556,7 +556,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'type': 'GuestIpAddress',
|
||||
{ 'struct': 'GuestIpAddress',
|
||||
'data': {'ip-address': 'str',
|
||||
'ip-address-type': 'GuestIpAddressType',
|
||||
'prefix': 'int'} }
|
||||
@@ -572,7 +572,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'type': 'GuestNetworkInterface',
|
||||
{ 'struct': 'GuestNetworkInterface',
|
||||
'data': {'name': 'str',
|
||||
'*hardware-address': 'str',
|
||||
'*ip-addresses': ['GuestIpAddress'] } }
|
||||
@@ -604,7 +604,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'GuestLogicalProcessor',
|
||||
{ 'struct': 'GuestLogicalProcessor',
|
||||
'data': {'logical-id': 'int',
|
||||
'online': 'bool',
|
||||
'*can-offline': 'bool'} }
|
||||
@@ -694,7 +694,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'GuestPCIAddress',
|
||||
{ 'struct': 'GuestPCIAddress',
|
||||
'data': {'domain': 'int', 'bus': 'int',
|
||||
'slot': 'int', 'function': 'int'} }
|
||||
|
||||
@@ -709,7 +709,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'GuestDiskAddress',
|
||||
{ 'struct': 'GuestDiskAddress',
|
||||
'data': {'pci-controller': 'GuestPCIAddress',
|
||||
'bus-type': 'GuestDiskBusType',
|
||||
'bus': 'int', 'target': 'int', 'unit': 'int'} }
|
||||
@@ -725,7 +725,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'GuestFilesystemInfo',
|
||||
{ 'struct': 'GuestFilesystemInfo',
|
||||
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
|
||||
'disk': ['GuestDiskAddress']} }
|
||||
|
||||
@@ -782,7 +782,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'GuestMemoryBlock',
|
||||
{ 'struct': 'GuestMemoryBlock',
|
||||
'data': {'phys-index': 'uint64',
|
||||
'online': 'bool',
|
||||
'*can-offline': 'bool'} }
|
||||
@@ -835,7 +835,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'GuestMemoryBlockResponse',
|
||||
{ 'struct': 'GuestMemoryBlockResponse',
|
||||
'data': { 'phys-index': 'uint64',
|
||||
'response': 'GuestMemoryBlockResponseType',
|
||||
'*error-code': 'int' }}
|
||||
@@ -876,7 +876,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'GuestMemoryBlockInfo',
|
||||
{ 'struct': 'GuestMemoryBlockInfo',
|
||||
'data': {'size': 'uint64'} }
|
||||
|
||||
##
|
||||
|
||||
@@ -45,15 +45,16 @@ NameInfo *qmp_query_name(Error **errp)
|
||||
|
||||
VersionInfo *qmp_query_version(Error **errp)
|
||||
{
|
||||
VersionInfo *info = g_malloc0(sizeof(*info));
|
||||
VersionInfo *info = g_new0(VersionInfo, 1);
|
||||
const char *version = QEMU_VERSION;
|
||||
char *tmp;
|
||||
|
||||
info->qemu.major = strtol(version, &tmp, 10);
|
||||
info->qemu = g_new0(VersionTriple, 1);
|
||||
info->qemu->major = strtol(version, &tmp, 10);
|
||||
tmp++;
|
||||
info->qemu.minor = strtol(tmp, &tmp, 10);
|
||||
info->qemu->minor = strtol(tmp, &tmp, 10);
|
||||
tmp++;
|
||||
info->qemu.micro = strtol(tmp, &tmp, 10);
|
||||
info->qemu->micro = strtol(tmp, &tmp, 10);
|
||||
info->package = g_strdup(QEMU_PKGVERSION);
|
||||
|
||||
return info;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# QAPI command marshaller generator
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
# Copyright (C) 2014 Red Hat, Inc.
|
||||
# Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
@@ -28,7 +28,7 @@ def type_visitor(name):
|
||||
|
||||
def generate_command_decl(name, args, ret_type):
|
||||
arglist=""
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
argtype = c_type(argtype, is_param=True)
|
||||
if optional:
|
||||
arglist += "bool has_%s, " % c_var(argname)
|
||||
@@ -53,7 +53,7 @@ def gen_sync_call(name, args, ret_type, indent=0):
|
||||
retval=""
|
||||
if ret_type:
|
||||
retval = "retval = "
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
arglist += "has_%s, " % c_var(argname)
|
||||
arglist += "%s, " % (c_var(argname))
|
||||
@@ -96,7 +96,7 @@ Visitor *v;
|
||||
def gen_visitor_input_vars_decl(args):
|
||||
ret = ""
|
||||
push_indent()
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(argname)s = false;
|
||||
@@ -139,7 +139,7 @@ v = qapi_dealloc_get_visitor(md);
|
||||
v = qmp_input_get_visitor(mi);
|
||||
''')
|
||||
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
||||
@@ -293,17 +293,12 @@ out:
|
||||
|
||||
return ret
|
||||
|
||||
def option_value_matches(opt, val, cmd):
|
||||
if opt in cmd and cmd[opt] == val:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gen_registry(commands):
|
||||
registry=""
|
||||
push_indent()
|
||||
for cmd in commands:
|
||||
options = 'QCO_NO_OPTIONS'
|
||||
if option_value_matches('success-response', 'no', cmd):
|
||||
if not cmd.get('success-response', True):
|
||||
options = 'QCO_NO_SUCCESS_RESP'
|
||||
|
||||
registry += mcgen('''
|
||||
|
||||
@@ -21,7 +21,7 @@ def _generate_event_api_name(event_name, params):
|
||||
l = len(api_name)
|
||||
|
||||
if params:
|
||||
for argname, argentry, optional, structured in parse_args(params):
|
||||
for argname, argentry, optional in parse_args(params):
|
||||
if optional:
|
||||
api_name += "bool has_%s,\n" % c_var(argname)
|
||||
api_name += "".ljust(l)
|
||||
@@ -93,7 +93,7 @@ def generate_event_implement(api_name, event_name, params):
|
||||
""",
|
||||
event_name = event_name)
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(params):
|
||||
for argname, argentry, optional in parse_args(params):
|
||||
if optional:
|
||||
ret += mcgen("""
|
||||
if (has_%(var)s) {
|
||||
|
||||
+36
-41
@@ -63,18 +63,13 @@ typedef struct %(name)sList
|
||||
def generate_struct_fields(members):
|
||||
ret = ''
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
for argname, argentry, optional in parse_args(members):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(c_name)s;
|
||||
''',
|
||||
c_name=c_var(argname))
|
||||
if structured:
|
||||
push_indent()
|
||||
ret += generate_struct({ "field": argname, "data": argentry})
|
||||
pop_indent()
|
||||
else:
|
||||
ret += mcgen('''
|
||||
ret += mcgen('''
|
||||
%(c_type)s %(c_name)s;
|
||||
''',
|
||||
c_type=c_type(argentry), c_name=c_var(argname))
|
||||
@@ -83,7 +78,7 @@ def generate_struct_fields(members):
|
||||
|
||||
def generate_struct(expr):
|
||||
|
||||
structname = expr.get('type', "")
|
||||
structname = expr.get('struct', "")
|
||||
fieldname = expr.get('field', "")
|
||||
members = expr['data']
|
||||
base = expr.get('base')
|
||||
@@ -170,9 +165,9 @@ typedef enum %(name)s
|
||||
|
||||
return lookup_decl + enum_decl
|
||||
|
||||
def generate_anon_union_qtypes(expr):
|
||||
def generate_alternate_qtypes(expr):
|
||||
|
||||
name = expr['union']
|
||||
name = expr['alternate']
|
||||
members = expr['data']
|
||||
|
||||
ret = mcgen('''
|
||||
@@ -181,17 +176,8 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
|
||||
name=name)
|
||||
|
||||
for key in members:
|
||||
qapi_type = members[key]
|
||||
if builtin_type_qtypes.has_key(qapi_type):
|
||||
qtype = builtin_type_qtypes[qapi_type]
|
||||
elif find_struct(qapi_type):
|
||||
qtype = "QTYPE_QDICT"
|
||||
elif find_union(qapi_type):
|
||||
qtype = "QTYPE_QDICT"
|
||||
elif find_enum(qapi_type):
|
||||
qtype = "QTYPE_QSTRING"
|
||||
else:
|
||||
assert False, "Invalid anonymous union member"
|
||||
qtype = find_alternate_member_qtype(members[key])
|
||||
assert qtype, "Invalid alternate member"
|
||||
|
||||
ret += mcgen('''
|
||||
[ %(qtype)s ] = %(abbrev)s_KIND_%(enum)s,
|
||||
@@ -206,9 +192,9 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
|
||||
return ret
|
||||
|
||||
|
||||
def generate_union(expr):
|
||||
def generate_union(expr, meta):
|
||||
|
||||
name = expr['union']
|
||||
name = expr[meta]
|
||||
typeinfo = expr['data']
|
||||
|
||||
base = expr.get('base')
|
||||
@@ -242,10 +228,9 @@ struct %(name)s
|
||||
''')
|
||||
|
||||
if base:
|
||||
base_fields = find_struct(base)['data']
|
||||
if discriminator:
|
||||
base_fields = base_fields.copy()
|
||||
del base_fields[discriminator]
|
||||
assert discriminator
|
||||
base_fields = find_struct(base)['data'].copy()
|
||||
del base_fields[discriminator]
|
||||
ret += generate_struct_fields(base_fields)
|
||||
else:
|
||||
assert not discriminator
|
||||
@@ -253,7 +238,7 @@ struct %(name)s
|
||||
ret += mcgen('''
|
||||
};
|
||||
''')
|
||||
if discriminator == {}:
|
||||
if meta == 'alternate':
|
||||
ret += mcgen('''
|
||||
extern const int %(name)s_qtypes[];
|
||||
''',
|
||||
@@ -398,14 +383,14 @@ exprs = parse_schema(input_file)
|
||||
exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
|
||||
|
||||
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdecl.write(generate_fwd_struct(typename, None, builtin_type=True))
|
||||
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
||||
|
||||
for expr in exprs:
|
||||
ret = "\n"
|
||||
if expr.has_key('type'):
|
||||
ret += generate_fwd_struct(expr['type'], expr['data'])
|
||||
if expr.has_key('struct'):
|
||||
ret += generate_fwd_struct(expr['struct'], expr['data'])
|
||||
elif expr.has_key('enum'):
|
||||
ret += generate_enum(expr['enum'], expr['data']) + "\n"
|
||||
ret += generate_fwd_enum_struct(expr['enum'], expr['data'])
|
||||
@@ -417,8 +402,12 @@ for expr in exprs:
|
||||
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
||||
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
|
||||
expr['data'].keys()))
|
||||
if expr.get('discriminator') == {}:
|
||||
fdef.write(generate_anon_union_qtypes(expr))
|
||||
elif expr.has_key('alternate'):
|
||||
ret += generate_fwd_struct(expr['alternate'], expr['data']) + "\n"
|
||||
ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
|
||||
fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
|
||||
expr['data'].keys()))
|
||||
fdef.write(generate_alternate_qtypes(expr))
|
||||
else:
|
||||
continue
|
||||
fdecl.write(ret)
|
||||
@@ -426,7 +415,7 @@ for expr in exprs:
|
||||
# to avoid header dependency hell, we always generate declarations
|
||||
# for built-in types in our header files and simply guard them
|
||||
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdecl.write(generate_type_cleanup_decl(typename + "List"))
|
||||
fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
||||
|
||||
@@ -435,24 +424,30 @@ fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
||||
# over these cases
|
||||
if do_builtins:
|
||||
fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdef.write(generate_type_cleanup(typename + "List"))
|
||||
fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
|
||||
|
||||
for expr in exprs:
|
||||
ret = "\n"
|
||||
if expr.has_key('type'):
|
||||
if expr.has_key('struct'):
|
||||
ret += generate_struct(expr) + "\n"
|
||||
ret += generate_type_cleanup_decl(expr['type'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['type'])
|
||||
fdef.write(generate_type_cleanup(expr['type']) + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['struct'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['struct'])
|
||||
fdef.write(generate_type_cleanup(expr['struct']) + "\n")
|
||||
elif expr.has_key('union'):
|
||||
ret += generate_union(expr)
|
||||
ret += generate_union(expr, 'union')
|
||||
ret += generate_type_cleanup_decl(expr['union'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['union'])
|
||||
fdef.write(generate_type_cleanup(expr['union']) + "\n")
|
||||
elif expr.has_key('alternate'):
|
||||
ret += generate_union(expr, 'alternate')
|
||||
ret += generate_type_cleanup_decl(expr['alternate'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['alternate'])
|
||||
fdef.write(generate_type_cleanup(expr['alternate']) + "\n")
|
||||
elif expr.has_key('enum'):
|
||||
ret += generate_type_cleanup_decl(expr['enum'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n")
|
||||
|
||||
+40
-88
@@ -43,79 +43,45 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
|
||||
''',
|
||||
c_type=type_name(type))
|
||||
|
||||
def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
|
||||
def generate_visit_struct_fields(name, members, base = None):
|
||||
substructs = []
|
||||
ret = ''
|
||||
if not fn_prefix:
|
||||
full_name = name
|
||||
else:
|
||||
full_name = "%s_%s" % (name, fn_prefix)
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
if structured:
|
||||
if not fn_prefix:
|
||||
nested_fn_prefix = argname
|
||||
else:
|
||||
nested_fn_prefix = "%s_%s" % (fn_prefix, argname)
|
||||
|
||||
nested_field_prefix = "%s%s." % (field_prefix, argname)
|
||||
ret += generate_visit_struct_fields(name, nested_field_prefix,
|
||||
nested_fn_prefix, argentry)
|
||||
ret += mcgen('''
|
||||
|
||||
static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp)
|
||||
{
|
||||
''',
|
||||
name=name, full_name=full_name, c_name=c_var(argname))
|
||||
ret += generate_visit_struct_body(full_name, argname, argentry)
|
||||
ret += mcgen('''
|
||||
}
|
||||
''')
|
||||
|
||||
if base:
|
||||
ret += generate_visit_implicit_struct(base)
|
||||
|
||||
ret += mcgen('''
|
||||
|
||||
static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
|
||||
static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
''',
|
||||
name=name, full_name=full_name)
|
||||
name=name)
|
||||
push_indent()
|
||||
|
||||
if base:
|
||||
ret += mcgen('''
|
||||
visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
|
||||
visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
|
||||
if (err) {
|
||||
goto out;
|
||||
}
|
||||
''',
|
||||
c_prefix=c_var(field_prefix),
|
||||
type=type_name(base), c_name=c_var('base'))
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
for argname, argentry, optional in parse_args(members):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
|
||||
if (!err && (*obj)->%(prefix)shas_%(c_name)s) {
|
||||
visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
|
||||
if (!err && (*obj)->has_%(c_name)s) {
|
||||
''',
|
||||
c_prefix=c_var(field_prefix), prefix=field_prefix,
|
||||
c_name=c_var(argname), name=argname)
|
||||
push_indent()
|
||||
|
||||
if structured:
|
||||
ret += mcgen('''
|
||||
visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err);
|
||||
ret += mcgen('''
|
||||
visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
|
||||
''',
|
||||
full_name=full_name, c_name=c_var(argname))
|
||||
else:
|
||||
ret += mcgen('''
|
||||
visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
|
||||
''',
|
||||
c_prefix=c_var(field_prefix), prefix=field_prefix,
|
||||
type=type_name(argentry), c_name=c_var(argname),
|
||||
name=argname)
|
||||
type=type_name(argentry), c_name=c_var(argname),
|
||||
name=argname)
|
||||
|
||||
if optional:
|
||||
pop_indent()
|
||||
@@ -141,29 +107,11 @@ out:
|
||||
return ret
|
||||
|
||||
|
||||
def generate_visit_struct_body(field_prefix, name, members):
|
||||
def generate_visit_struct_body(name, members):
|
||||
ret = mcgen('''
|
||||
Error *err = NULL;
|
||||
|
||||
''')
|
||||
|
||||
if not field_prefix:
|
||||
full_name = name
|
||||
else:
|
||||
full_name = "%s_%s" % (field_prefix, name)
|
||||
|
||||
if len(field_prefix):
|
||||
ret += mcgen('''
|
||||
visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
|
||||
''',
|
||||
name=name)
|
||||
else:
|
||||
ret += mcgen('''
|
||||
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
||||
''',
|
||||
name=name)
|
||||
|
||||
ret += mcgen('''
|
||||
if (!err) {
|
||||
if (*obj) {
|
||||
visit_type_%(name)s_fields(m, obj, errp);
|
||||
@@ -172,17 +120,17 @@ def generate_visit_struct_body(field_prefix, name, members):
|
||||
}
|
||||
error_propagate(errp, err);
|
||||
''',
|
||||
name=full_name)
|
||||
name=name)
|
||||
|
||||
return ret
|
||||
|
||||
def generate_visit_struct(expr):
|
||||
|
||||
name = expr['type']
|
||||
name = expr['struct']
|
||||
members = expr['data']
|
||||
base = expr.get('base')
|
||||
|
||||
ret = generate_visit_struct_fields(name, "", "", members, base)
|
||||
ret = generate_visit_struct_fields(name, members, base)
|
||||
|
||||
ret += mcgen('''
|
||||
|
||||
@@ -191,7 +139,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
|
||||
''',
|
||||
name=name)
|
||||
|
||||
ret += generate_visit_struct_body("", name, members)
|
||||
ret += generate_visit_struct_body(name, members)
|
||||
|
||||
ret += mcgen('''
|
||||
}
|
||||
@@ -237,7 +185,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **er
|
||||
''',
|
||||
name=name)
|
||||
|
||||
def generate_visit_anon_union(name, members):
|
||||
def generate_visit_alternate(name, members):
|
||||
ret = mcgen('''
|
||||
|
||||
void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
|
||||
@@ -256,15 +204,15 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
|
||||
''',
|
||||
name=name)
|
||||
|
||||
# For anon union, always use the default enum type automatically generated
|
||||
# For alternate, always use the default enum type automatically generated
|
||||
# as "'%sKind' % (name)"
|
||||
disc_type = '%sKind' % (name)
|
||||
|
||||
for key in members:
|
||||
assert (members[key] in builtin_types
|
||||
assert (members[key] in builtin_types.keys()
|
||||
or find_struct(members[key])
|
||||
or find_union(members[key])
|
||||
or find_enum(members[key])), "Invalid anonymous union member"
|
||||
or find_enum(members[key])), "Invalid alternate member"
|
||||
|
||||
enum_full_value = generate_enum_full_value(disc_type, key)
|
||||
ret += mcgen('''
|
||||
@@ -300,27 +248,22 @@ def generate_visit_union(expr):
|
||||
base = expr.get('base')
|
||||
discriminator = expr.get('discriminator')
|
||||
|
||||
if discriminator == {}:
|
||||
assert not base
|
||||
return generate_visit_anon_union(name, members)
|
||||
|
||||
enum_define = discriminator_find_enum_define(expr)
|
||||
if enum_define:
|
||||
# Use the enum type as discriminator
|
||||
ret = ""
|
||||
disc_type = enum_define['enum_name']
|
||||
else:
|
||||
# There will always be a discriminator in the C switch code, by default it
|
||||
# is an enum type generated silently as "'%sKind' % (name)"
|
||||
# There will always be a discriminator in the C switch code, by default
|
||||
# it is an enum type generated silently as "'%sKind' % (name)"
|
||||
ret = generate_visit_enum('%sKind' % name, members.keys())
|
||||
disc_type = '%sKind' % (name)
|
||||
|
||||
if base:
|
||||
base_fields = find_struct(base)['data']
|
||||
if discriminator:
|
||||
base_fields = base_fields.copy()
|
||||
del base_fields[discriminator]
|
||||
ret += generate_visit_struct_fields(name, "", "", base_fields)
|
||||
assert discriminator
|
||||
base_fields = find_struct(base)['data'].copy()
|
||||
del base_fields[discriminator]
|
||||
ret += generate_visit_struct_fields(name, base_fields)
|
||||
|
||||
if discriminator:
|
||||
for key in members:
|
||||
@@ -538,7 +481,7 @@ exprs = parse_schema(input_file)
|
||||
# to avoid header dependency hell, we always generate declarations
|
||||
# for built-in types in our header files and simply guard them
|
||||
fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdecl.write(generate_declaration(typename, None, builtin_type=True))
|
||||
fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
|
||||
|
||||
@@ -546,16 +489,16 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
|
||||
# have the functions defined, so we use -b option to provide control
|
||||
# over these cases
|
||||
if do_builtins:
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdef.write(generate_visit_list(typename, None))
|
||||
|
||||
for expr in exprs:
|
||||
if expr.has_key('type'):
|
||||
if expr.has_key('struct'):
|
||||
ret = generate_visit_struct(expr)
|
||||
ret += generate_visit_list(expr['type'], expr['data'])
|
||||
ret += generate_visit_list(expr['struct'], expr['data'])
|
||||
fdef.write(ret)
|
||||
|
||||
ret = generate_declaration(expr['type'], expr['data'])
|
||||
ret = generate_declaration(expr['struct'], expr['data'])
|
||||
fdecl.write(ret)
|
||||
elif expr.has_key('union'):
|
||||
ret = generate_visit_union(expr)
|
||||
@@ -569,6 +512,15 @@ for expr in exprs:
|
||||
expr['data'].keys())
|
||||
ret += generate_declaration(expr['union'], expr['data'])
|
||||
fdecl.write(ret)
|
||||
elif expr.has_key('alternate'):
|
||||
ret = generate_visit_alternate(expr['alternate'], expr['data'])
|
||||
ret += generate_visit_list(expr['alternate'], expr['data'])
|
||||
fdef.write(ret)
|
||||
|
||||
ret = generate_decl_enum('%sKind' % expr['alternate'],
|
||||
expr['data'].keys())
|
||||
ret += generate_declaration(expr['alternate'], expr['data'])
|
||||
fdecl.write(ret)
|
||||
elif expr.has_key('enum'):
|
||||
ret = generate_visit_list(expr['enum'], expr['data'])
|
||||
ret += generate_visit_enum(expr['enum'], expr['data'])
|
||||
|
||||
+447
-81
File diff suppressed because it is too large
Load Diff
+31
-7
@@ -207,20 +207,44 @@ $(foreach target,$(SYSEMU_TARGET_LIST), \
|
||||
$(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF))))
|
||||
|
||||
check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
|
||||
comments.json empty.json funny-char.json indented-expr.json \
|
||||
missing-colon.json missing-comma-list.json \
|
||||
missing-comma-object.json non-objects.json \
|
||||
comments.json empty.json enum-empty.json enum-missing-data.json \
|
||||
enum-wrong-data.json enum-int-member.json enum-dict-member.json \
|
||||
enum-clash-member.json enum-max-member.json enum-union-clash.json \
|
||||
enum-bad-name.json funny-char.json indented-expr.json \
|
||||
missing-type.json bad-ident.json ident-with-escape.json \
|
||||
escape-outside-string.json unknown-escape.json \
|
||||
escape-too-short.json escape-too-big.json unicode-str.json \
|
||||
double-type.json bad-base.json bad-type-bool.json bad-type-int.json \
|
||||
bad-type-dict.json double-data.json unknown-expr-key.json \
|
||||
redefined-type.json redefined-command.json redefined-builtin.json \
|
||||
redefined-event.json command-int.json bad-data.json event-max.json \
|
||||
type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
|
||||
data-array-empty.json data-array-unknown.json data-int.json \
|
||||
data-unknown.json data-member-unknown.json data-member-array.json \
|
||||
data-member-array-bad.json returns-array-bad.json returns-int.json \
|
||||
returns-unknown.json returns-alternate.json returns-whitelist.json \
|
||||
missing-colon.json missing-comma-list.json missing-comma-object.json \
|
||||
nested-struct-data.json nested-struct-returns.json non-objects.json \
|
||||
qapi-schema-test.json quoted-structural-chars.json \
|
||||
trailing-comma-list.json trailing-comma-object.json \
|
||||
unclosed-list.json unclosed-object.json unclosed-string.json \
|
||||
duplicate-key.json union-invalid-base.json flat-union-no-base.json \
|
||||
flat-union-invalid-discriminator.json \
|
||||
duplicate-key.json union-invalid-base.json union-bad-branch.json \
|
||||
union-optional-branch.json union-unknown.json union-max.json \
|
||||
flat-union-optional-discriminator.json flat-union-no-base.json \
|
||||
flat-union-invalid-discriminator.json flat-union-inline.json \
|
||||
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
|
||||
flat-union-string-discriminator.json \
|
||||
flat-union-string-discriminator.json union-base-no-discriminator.json \
|
||||
flat-union-bad-discriminator.json flat-union-bad-base.json \
|
||||
flat-union-base-star.json flat-union-int-branch.json \
|
||||
flat-union-base-union.json flat-union-branch-clash.json \
|
||||
alternate-nested.json alternate-unknown.json alternate-clash.json \
|
||||
alternate-good.json alternate-base.json alternate-array.json \
|
||||
alternate-conflict-string.json alternate-conflict-dict.json \
|
||||
include-simple.json include-relpath.json include-format-err.json \
|
||||
include-non-file.json include-no-file.json include-before-err.json \
|
||||
include-nested-err.json include-self-cycle.json include-cycle.json \
|
||||
include-repetition.json event-nest-struct.json)
|
||||
include-repetition.json event-nest-struct.json event-case.json \
|
||||
struct-base-clash.json struct-base-clash-deep.json )
|
||||
|
||||
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
|
||||
tests/test-qmp-commands.h tests/test-qapi-event.h
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,7 @@
|
||||
# we do not allow array branches in alternates
|
||||
# TODO: should we support this?
|
||||
{ 'struct': 'One',
|
||||
'data': { 'name': 'str' } }
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'One',
|
||||
'two': [ 'int' ] } }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user