提交 9e72681d 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2015-09-21' into staging

qapi: QMP introspection

# gpg: Signature made Mon 21 Sep 2015 08:59:17 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-qapi-2015-09-21: (26 commits)
  qapi-introspect: Hide type names
  qapi: New QMP command query-qmp-schema for QMP introspection
  qapi: Pseudo-type '**' is now unused, drop it
  qapi-schema: Fix up misleading specification of netdev_add
  qom: Don't use 'gen': false for qom-get, qom-set, object-add
  qapi: Introduce a first class 'any' type
  qapi: Make output visitor return qnull() instead of NULL
  qapi: Improve built-in type documentation
  qapi-commands: De-duplicate output marshaling functions
  qapi: De-duplicate parameter list generation
  qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO()
  qapi-commands: Rearrange code
  qapi-visit: Rearrange code a bit
  qapi: Clean up after recent conversions to QAPISchemaVisitor
  qapi: Replace dirty is_c_ptr() by method c_null()
  qapi-event: Convert to QAPISchemaVisitor, fixing data with base
  qapi-event: Eliminate global variable event_enum_value
  qapi: De-duplicate enum code generation
  qapi-commands: Convert to QAPISchemaVisitor
  qapi-visit: Convert to QAPISchemaVisitor, fixing bugs
  ...
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
......@@ -34,6 +34,7 @@
/qapi-visit.[ch]
/qapi-event.[ch]
/qmp-commands.h
/qmp-introspect.[ch]
/qmp-marshal.c
/qemu-doc.html
/qemu-tech.html
......
......@@ -52,6 +52,8 @@ endif
GENERATED_HEADERS = config-host.h qemu-options.def
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
GENERATED_HEADERS += qmp-introspect.h
GENERATED_SOURCES += qmp-introspect.c
GENERATED_HEADERS += trace/generated-events.h
GENERATED_SOURCES += trace/generated-events.c
......@@ -269,7 +271,7 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
$(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
$(SRC_PATH)/qapi/event.json
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
qapi-types.c qapi-types.h :\
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
......@@ -291,6 +293,11 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
$(gen-out-type) -o "." -m $<, \
" GEN $@")
qmp-introspect.h qmp-introspect.c :\
$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
$(gen-out-type) -o "." $<, \
" GEN $@")
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
......
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/
util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
util-obj-y = util/ qobject/ qapi/
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
......@@ -92,6 +93,7 @@ common-obj-$(CONFIG_FDT) += device_tree.o
# qapi
common-obj-y += qmp-marshal.o
common-obj-y += qmp-introspect.o
common-obj-y += qmp.o hmp.o
endif
......
......@@ -111,10 +111,7 @@ and field names within a type, should be all lower case with words
separated by a hyphen. However, some existing older commands and
complex types use underscore; when extending such expressions,
consistency is preferred over blindly avoiding underscore. Event
names should be ALL_CAPS with words separated by underscore. The
special string '**' appears for some commands that manually perform
their own type checking rather than relying on the type-safe code
produced by the qapi code generators.
names should be ALL_CAPS with words separated by underscore.
Any name (command, event, type, field, or enum value) beginning with
"x-" is marked experimental, and may be withdrawn or changed
......@@ -140,17 +137,25 @@ must have a value that forms a struct name.
=== Built-in Types ===
The following types are built-in to the parser:
'str' - arbitrary UTF-8 string
'int' - 64-bit signed integer (although the C code may place further
restrictions on acceptable range)
'number' - floating point number
'bool' - JSON value of true or false
'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum
bit size
'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts
'size' - like 'uint64', but allows scaled suffix from command line
visitor
The following types are predefined, and map to C as follows:
Schema C JSON
str char * any JSON string, UTF-8
number double any JSON number
int int64_t a JSON number without fractional part
that fits into the C integer type
int8 int8_t likewise
int16 int16_t likewise
int32 int32_t likewise
int64 int64_t likewise
uint8 uint8_t likewise
uint16 uint16_t likewise
uint32 uint32_t likewise
uint64 uint64_t likewise
size uint64_t like uint64_t, except StringInputVisitor
accepts size suffixes
bool bool JSON true or false
any QObject * any JSON value
=== Includes ===
......@@ -453,17 +458,14 @@ which would validate this Client JSON Protocol transaction:
<= { "return": [ { "value": "one" }, { } ] }
In rare cases, QAPI cannot express a type-safe representation of a
corresponding Client JSON Protocol command. In these cases, if the
command expression includes the key 'gen' with boolean value false,
then the 'data' or 'returns' member that intends to bypass generated
type-safety and do its own manual validation should use an inline
dictionary definition, with a value of '**' rather than a valid type
name for the keys that the generated code will not validate. Please
try to avoid adding new commands that rely on this, and instead use
type-safe unions. For an example of bypass usage:
corresponding Client JSON Protocol command. You then have to suppress
generation of a marshalling function by including a key 'gen' with
boolean value false, and instead write your own function. Please try
to avoid adding new commands that rely on this, and instead use
type-safe unions. For an example of this usage:
{ 'command': 'netdev_add',
'data': {'type': 'str', 'id': 'str', '*props': '**'},
'data': {'type': 'str', 'id': 'str'},
'gen': false }
Normally, the QAPI schema is used to describe synchronous exchanges,
......@@ -500,13 +502,204 @@ Resulting in this JSON object:
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
== Client JSON Protocol introspection ==
Clients of a Client JSON Protocol commonly need to figure out what
exactly the server (QEMU) supports.
For this purpose, QMP provides introspection via command
query-qmp-schema. QGA currently doesn't support introspection.
query-qmp-schema returns a JSON array of SchemaInfo objects. These
objects together describe the wire ABI, as defined in the QAPI schema.
However, the SchemaInfo can't reflect all the rules and restrictions
that apply to QMP. It's interface introspection (figuring out what's
there), not interface specification. The specification is in the QAPI
schema. To understand how QMP is to be used, you need to study the
QAPI schema.
Like any other command, query-qmp-schema is itself defined in the QAPI
schema, along with the SchemaInfo type. This text attempts to give an
overview how things work. For details you need to consult the QAPI
schema.
SchemaInfo objects have common members "name" and "meta-type", and
additional variant members depending on the value of meta-type.
Each SchemaInfo object describes a wire ABI entity of a certain
meta-type: a command, event or one of several kinds of type.
SchemaInfo for commands and events have the same name as in the QAPI
schema.
Command and event names are part of the wire ABI, but type names are
not. Therefore, the SchemaInfo for types have auto-generated
meaningless names. For readability, the examples in this section use
meaningful type names instead.
To examine a type, start with a command or event using it, then follow
references by name.
QAPI schema definitions not reachable that way are omitted.
The SchemaInfo for a command has meta-type "command", and variant
members "arg-type" and "ret-type". On the wire, the "arguments"
member of a client's "execute" command must conform to the object type
named by "arg-type". The "return" member that the server passes in a
success response conforms to the type named by "ret-type".
If the command takes no arguments, "arg-type" names an object type
without members. Likewise, if the command returns nothing, "ret-type"
names an object type without members.
Example: the SchemaInfo for command query-qmp-schema
{ "name": "query-qmp-schema", "meta-type": "command",
"arg-type": ":empty", "ret-type": "SchemaInfoList" }
Type ":empty" is an object type without members, and type
"SchemaInfoList" is the array of SchemaInfo type.
The SchemaInfo for an event has meta-type "event", and variant member
"arg-type". On the wire, a "data" member that the server passes in an
event conforms to the object type named by "arg-type".
If the event carries no additional information, "arg-type" names an
object type without members. The event may not have a data member on
the wire then.
Each command or event defined with dictionary-valued 'data' in the
QAPI schema implicitly defines an object type.
Example: the SchemaInfo for EVENT_C from section Events
{ "name": "EVENT_C", "meta-type": "event",
"arg-type": ":obj-EVENT_C-arg" }
Type ":obj-EVENT_C-arg" is an implicitly defined object type with
the two members from the event's definition.
The SchemaInfo for struct and union types has meta-type "object".
The SchemaInfo for a struct type has variant member "members".
The SchemaInfo for a union type additionally has variant members "tag"
and "variants".
"members" is a JSON array describing the object's common members, if
any. Each element is a JSON object with members "name" (the member's
name), "type" (the name of its type), and optionally "default". The
member is optional if "default" is present. Currently, "default" can
only have value null. Other values are reserved for future
extensions.
Example: the SchemaInfo for MyType from section Struct types
{ "name": "MyType", "meta-type": "object",
"members": [
{ "name": "member1", "type": "str" },
{ "name": "member2", "type": "int" },
{ "name": "member3", "type": "str", "default": null } ] }
"tag" is the name of the common member serving as type tag.
"variants" is a JSON array describing the object's variant members.
Each element is a JSON object with members "case" (the value of type
tag this element applies to) and "type" (the name of an object type
that provides the variant members for this type tag value).
Example: the SchemaInfo for flat union BlockdevOptions from section
Union types
{ "name": "BlockdevOptions", "meta-type": "object",
"members": [
{ "name": "driver", "type": "BlockdevDriver" },
{ "name": "readonly", "type": "bool"} ],
"tag": "driver",
"variants": [
{ "case": "file", "type": "FileOptions" },
{ "case": "qcow2", "type": "Qcow2Options" } ] }
Note that base types are "flattened": its members are included in the
"members" array.
A simple union implicitly defines an enumeration type for its implicit
discriminator (called "type" on the wire, see section Union types).
A simple union implicitly defines an object type for each of its
variants.
Example: the SchemaInfo for simple union BlockdevOptions from section
Union types
{ "name": "BlockdevOptions", "meta-type": "object",
"members": [
{ "name": "kind", "type": "BlockdevOptionsKind" } ],
"tag": "type",
"variants": [
{ "case": "file", "type": ":obj-FileOptions-wrapper" },
{ "case": "qcow2", "type": ":obj-Qcow2Options-wrapper" } ] }
Enumeration type "BlockdevOptionsKind" and the object types
":obj-FileOptions-wrapper", ":obj-Qcow2Options-wrapper" are
implicitly defined.
The SchemaInfo for an alternate type has meta-type "alternate", and
variant member "members". "members" is a JSON array. Each element is
a JSON object with member "type", which names a type. Values of the
alternate type conform to exactly one of its member types.
Example: the SchemaInfo for BlockRef from section Alternate types
{ "name": "BlockRef", "meta-type": "alternate",
"members": [
{ "type": "BlockdevOptions" },
{ "type": "str" } ] }
The SchemaInfo for an array type has meta-type "array", and variant
member "element-type", which names the array's element type. Array
types are implicitly defined.
Example: the SchemaInfo for ['str']
{ "name": "strList", "meta-type": "array",
"element-type": "str" }
The SchemaInfo for an enumeration type has meta-type "enum" and
variant member "values".
Example: the SchemaInfo for MyEnum from section Enumeration types
{ "name": "MyEnum", "meta-type": "enum",
"values": [ "value1", "value2", "value3" ] }
The SchemaInfo for a built-in type has the same name as the type in
the QAPI schema (see section Built-in Types), with one exception
detailed below. It has variant member "json-type" that shows how
values of this type are encoded on the wire.
Example: the SchemaInfo for str
{ "name": "str", "meta-type": "builtin", "json-type": "string" }
The QAPI schema supports a number of integer types that only differ in
how they map to C. They are identical as far as SchemaInfo is
concerned. Therefore, they get all mapped to a single type "int" in
SchemaInfo.
As explained above, type names are not part of the wire ABI. Not even
the names of built-in types. Clients should examine member
"json-type" instead of hard-coding names of built-in types.
== Code generation ==
Schemas are fed into 3 scripts to generate all the code/files that, paired
with the core QAPI libraries, comprise everything required to take JSON
commands read in by a Client JSON Protocol server, unmarshal the arguments into
the underlying C types, call into the corresponding C function, and map the
response back to a Client JSON Protocol response to be returned to the user.
Schemas are fed into four scripts to generate all the code/files that,
paired with the core QAPI libraries, comprise everything required to
take JSON commands read in by a Client JSON Protocol server, unmarshal
the arguments into the underlying C types, call into the corresponding
C function, and map the response back to a Client JSON Protocol
response to be returned to the user.
As an example, we'll use the following schema, which describes a single
complex user-defined type (which will produce a C struct, along with a list
......@@ -545,7 +738,7 @@ Example:
$ cat qapi-generated/example-qapi-types.c
[Uninteresting stuff omitted...]
void qapi_free_UserDefOneList(UserDefOneList *obj)
void qapi_free_UserDefOne(UserDefOne *obj)
{
QapiDeallocVisitor *md;
Visitor *v;
......@@ -556,12 +749,11 @@ Example:
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
visit_type_UserDefOneList(v, &obj, NULL, NULL);
visit_type_UserDefOne(v, &obj, NULL, NULL);
qapi_dealloc_visitor_cleanup(md);
}
void qapi_free_UserDefOne(UserDefOne *obj)
void qapi_free_UserDefOneList(UserDefOneList *obj)
{
QapiDeallocVisitor *md;
Visitor *v;
......@@ -572,7 +764,7 @@ Example:
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
visit_type_UserDefOne(v, &obj, NULL, NULL);
visit_type_UserDefOneList(v, &obj, NULL, NULL);
qapi_dealloc_visitor_cleanup(md);
}
$ cat qapi-generated/example-qapi-types.h
......@@ -585,25 +777,25 @@ Example:
typedef struct UserDefOne UserDefOne;
typedef struct UserDefOneList {
union {
UserDefOne *value;
uint64_t padding;
};
struct UserDefOneList *next;
} UserDefOneList;
[Functions on built-in types omitted...]
typedef struct UserDefOneList UserDefOneList;
struct UserDefOne {
int64_t integer;
char *string;
};
void qapi_free_UserDefOneList(UserDefOneList *obj);
void qapi_free_UserDefOne(UserDefOne *obj);
struct UserDefOneList {
union {
UserDefOne *value;
uint64_t padding;
};
UserDefOneList *next;
};
void qapi_free_UserDefOneList(UserDefOneList *obj);
#endif
=== scripts/qapi-visit.py ===
......@@ -722,7 +914,7 @@ Example:
$ cat qapi-generated/example-qmp-marshal.c
[Uninteresting stuff omitted...]
static void qmp_marshal_output_my_command(UserDefOne *ret_in, QObject **ret_out, Error **errp)
static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp)
{
Error *local_err = NULL;
QmpOutputVisitor *mo = qmp_output_visitor_new();
......@@ -745,7 +937,7 @@ Example:
qapi_dealloc_visitor_cleanup(md);
}
static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp)
static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp)
{
Error *local_err = NULL;
UserDefOne *retval;
......@@ -765,7 +957,7 @@ Example:
goto out;
}
qmp_marshal_output_my_command(retval, ret, &local_err);
qmp_marshal_output_UserDefOne(retval, ret, &local_err);
out:
error_propagate(errp, local_err);
......@@ -778,7 +970,7 @@ Example:
static void qmp_init_marshal(void)
{
qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS);
qmp_register_command("my-command", qmp_marshal_my_command, QCO_NO_OPTIONS);
}
qapi_init(qmp_init_marshal);
......@@ -830,9 +1022,9 @@ Example:
QDECREF(qmp);
}
const char *example_QAPIEvent_lookup[] = {
"MY_EVENT",
NULL,
const char *const example_QAPIEvent_lookup[] = {
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
[EXAMPLE_QAPI_EVENT_MAX] = NULL,
};
$ cat qapi-generated/example-qapi-event.h
[Uninteresting stuff omitted...]
......@@ -847,10 +1039,45 @@ Example:
void qapi_event_send_my_event(Error **errp);
extern const char *example_QAPIEvent_lookup[];
typedef enum example_QAPIEvent {
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
EXAMPLE_QAPI_EVENT_MAX = 1,
} example_QAPIEvent;
extern const char *const example_QAPIEvent_lookup[];
#endif
=== scripts/qapi-introspect.py ===
Used to generate the introspection C code for a schema. The following
files are created:
$(prefix)qmp-introspect.c - Defines a string holding a JSON
description of the schema.
$(prefix)qmp-introspect.h - Declares the above string.
Example:
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
--prefix="example-" example-schema.json
$ cat qapi-generated/example-qmp-introspect.c
[Uninteresting stuff omitted...]
const char example_qmp_schema_json[] = "["
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
"{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
$ cat qapi-generated/example-qmp-introspect.h
[Uninteresting stuff omitted...]
#ifndef EXAMPLE_QMP_INTROSPECT_H
#define EXAMPLE_QMP_INTROSPECT_H
extern const char example_qmp_schema_json[];
#endif
......@@ -127,7 +127,7 @@ following at the bottom:
{
.name = "hello-world",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_hello_world,
.mhandler.cmd_new = qmp_marshal_hello_world,
},
You're done. Now build qemu, run it as suggested in the "Testing" section,
......@@ -179,7 +179,7 @@ The last step is to update the qmp-commands.hx file:
{
.name = "hello-world",
.args_type = "message:s?",
.mhandler.cmd_new = qmp_marshal_input_hello_world,
.mhandler.cmd_new = qmp_marshal_hello_world,
},
Notice that the "args_type" member got our "message" argument. The character
......@@ -461,7 +461,7 @@ The last step is to add the correspoding entry in the qmp-commands.hx file:
{
.name = "query-alarm-clock",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_query_alarm_clock,
.mhandler.cmd_new = qmp_marshal_query_alarm_clock,
},
Time to test the new command. Build qemu, run it as described in the "Testing"
......@@ -607,7 +607,7 @@ To test this you have to add the corresponding qmp-commands.hx entry:
{
.name = "query-alarm-methods",
.args_type = "",
.mhandler.cmd_new = qmp_marshal_input_query_alarm_methods,
.mhandler.cmd_new = qmp_marshal_query_alarm_methods,
},
Now Build qemu, run it as explained in the "Testing" section and try our new
......
......@@ -42,9 +42,6 @@ void monitor_read_command(Monitor *mon, int show_prompt);
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
void *opaque);
void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp);
void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp);
void qmp_object_add(QDict *qdict, QObject **ret, Error **errp);
void object_add(const char *type, const char *id, const QDict *qdict,
Visitor *v, Error **errp);
......
......@@ -40,6 +40,8 @@ struct Visitor
void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
void (*type_number)(Visitor *v, double *obj, const char *name,
Error **errp);
void (*type_any)(Visitor *v, QObject **obj, const char *name,
Error **errp);
/* May be NULL */
void (*optional)(Visitor *v, bool *present, const char *name,
......
......@@ -58,6 +58,7 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp);
bool visit_start_union(Visitor *v, bool data_present, Error **errp);
void visit_end_union(Visitor *v, bool data_present, Error **errp);
......
......@@ -74,6 +74,7 @@
#include "block/qapi.h"
#include "qapi/qmp-event.h"
#include "qapi-event.h"
#include "qmp-introspect.h"
#include "sysemu/block-backend.h"
/* for hmp_info_irq/pic */
......@@ -928,6 +929,21 @@ EventInfoList *qmp_query_events(Error **errp)
return ev_list;
}
/*
* Minor hack: generated marshalling suppressed for this command
* ('gen': false in the schema) so we can parse the JSON string
* directly into QObject instead of first parsing it with
* visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it
* to QObject with generated output marshallers, every time. Instead,
* we do it in test-qmp-input-visitor.c, just to make sure
* qapi-introspect.py's output actually conforms to the schema.
*/
static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp)
{
*ret_data = qobject_from_json(qmp_schema_json);
}
/* set the current CPU defined by the user */
int monitor_set_cpu(int cpu_index)
{
......@@ -3912,7 +3928,7 @@ static QObject *get_qmp_greeting(void)
{
QObject *ver = NULL;
qmp_marshal_input_query_version(NULL, &ver, NULL);
qmp_marshal_query_version(NULL, &ver, NULL);
return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
}
......
......@@ -17,6 +17,9 @@
# Tracing commands
{ 'include': 'qapi/trace.json' }
# QAPI introspection
{ 'include': 'qapi/introspect.json' }
##
# @LostTickPolicy:
#
......@@ -1698,8 +1701,7 @@
##
{ 'command': 'qom-get',
'data': { 'path': 'str', 'property': 'str' },
'returns': '**',
'gen': false }
'returns': 'any' }
##
# @qom-set:
......@@ -1716,8 +1718,7 @@
# Since: 1.2
##
{ 'command': 'qom-set',
'data': { 'path': 'str', 'property': 'str', 'value': '**' },
'gen': false }
'data': { 'path': 'str', 'property': 'str', 'value': 'any' } }
##
# @set_password:
......@@ -2081,11 +2082,12 @@
#
# @id: the name of the new network backend
#
# @props: #optional a list of properties to be passed to the backend in
# the format 'name=value', like 'ifname=tap0,script=no'
# Additional arguments depend on the type.
#
# Notes: The semantics of @props is not well defined. Future commands will be
# introduced that provide stronger typing for backend creation.
# TODO This command effectively bypasses QAPI completely due to its
# "additional arguments" business. It shouldn't have been added to
# the schema in this form. It should be qapified properly, or
# replaced by a properly qapified command.
#
# Since: 0.14.0
#
......@@ -2093,8 +2095,8 @@
# If @type is not a valid network backend, DeviceNotFound
##
{ 'command': 'netdev_add',
'data': {'type': 'str', 'id': 'str', '*props': '**'},
'gen': false }
'data': {'type': 'str', 'id': 'str'},
'gen': false } # so we can get the additional arguments
##
# @netdev_del:
......@@ -2127,8 +2129,7 @@
# Since: 2.0
##
{ 'command': 'object-add',
'data': {'qom-type': 'str', 'id': 'str', '*props': '**'},
'gen': false }
'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} }
##
# @object-del:
......
# -*- Mode: Python -*-
#
# QAPI/QMP introspection
#
# Copyright (C) 2015 Red Hat, Inc.
#
# Authors:
# Markus Armbruster <armbru@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2 or later.
# See the COPYING file in the top-level directory.
##
# @query-qmp-schema
#
# Command query-qmp-schema exposes the QMP wire ABI as an array of
# SchemaInfo. This lets QMP clients figure out what commands and
# events are available in this QEMU, and their parameters and results.
#
# However, the SchemaInfo can't reflect all the rules and restrictions
# that apply to QMP. It's interface introspection (figuring out
# what's there), not interface specification. The specification is in
# the QAPI schema.
#
# Returns: array of @SchemaInfo, where each element describes an
# entity in the ABI: command, event, type, ...
#
# Note: the QAPI schema is also used to help define *internal*
# interfaces, by defining QAPI types. These are not part of the QMP
# wire ABI, and therefore not returned by this command.
#
# Since: 2.5
##
{ 'command': 'query-qmp-schema',
'returns': [ 'SchemaInfo' ],
'gen': false } # just to simplify qmp_query_json()
##
# @SchemaMetaType
#
# This is a @SchemaInfo's meta type, i.e. the kind of entity it
# describes.
#
# @builtin: a predefined type such as 'int' or 'bool'.
#
# @enum: an enumeration type
#
# @array: an array type
#
# @object: an object type (struct or union)
#
# @alternate: an alternate type
#
# @command: a QMP command
#
# @event: a QMP event
#
# Since: 2.5
##
{ 'enum': 'SchemaMetaType',
'data': [ 'builtin', 'enum', 'array', 'object', 'alternate',
'command', 'event' ] }
##
# @SchemaInfoBase
#
# Members common to any @SchemaInfo.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoBase',
'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
##
# @SchemaInfo
#
# @name: the entity's name, inherited from @base.
# Commands and events have the name defined in the QAPI schema.
# Unlike command and event names, type names are not part of
# the wire ABI. Consequently, type names are meaningless
# strings here.
#
# All references to other SchemaInfo are by name.
#
# @meta-type: the entity's meta type, inherited from @base.
#
# Additional members depend on the value of @meta-type.
#
# Since: 2.5
##
{ 'union': 'SchemaInfo',
'base': 'SchemaInfoBase',
'discriminator': 'meta-type',
'data': {
'builtin': 'SchemaInfoBuiltin',
'enum': 'SchemaInfoEnum',
'array': 'SchemaInfoArray',
'object': 'SchemaInfoObject',
'alternate': 'SchemaInfoAlternate',
'command': 'SchemaInfoCommand',
'event': 'SchemaInfoEvent' } }
##
# @SchemaInfoBuiltin
#
# Additional SchemaInfo members for meta-type 'builtin'.
#
# @json-type: the JSON type used for this type on the wire.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoBuiltin',
'data': { 'json-type': 'JSONType' } }
##
# @JSONType
#
# The four primitive and two structured types according to RFC 7159
# section 1, plus 'int' (split off 'number'), plus the obvious top
# type 'value'.
#
# Since: 2.5
##
{ 'enum': 'JSONType',
'data': [ 'string', 'number', 'int', 'boolean', 'null',
'object', 'array', 'value' ] }
##
# @SchemaInfoEnum
#
# Additional SchemaInfo members for meta-type 'enum'.
#
# @values: the enumeration type's values.
#
# Values of this type are JSON string on the wire.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoEnum',
'data': { 'values': ['str'] } }
##
# @SchemaInfoArray
#
# Additional SchemaInfo members for meta-type 'array'.
#
# @element-type: the array type's element type.
#
# Values of this type are JSON array on the wire.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoArray',
'data': { 'element-type': 'str' } }
##
# @SchemaInfoObject
#
# Additional SchemaInfo members for meta-type 'object'.
#
# @members: the object type's (non-variant) members.
#
# @tag: #optional the name of the member serving as type tag.
# An element of @members with this name must exist.
#
# @variants: #optional variant members, i.e. additional members that
# depend on the type tag's value. Present exactly when
# @tag is present.
#
# Values of this type are JSON object on the wire.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoObject',
'data': { 'members': [ 'SchemaInfoObjectMember' ],
'*tag': 'str',
'*variants': [ 'SchemaInfoObjectVariant' ] } }
##
# @SchemaInfoObjectMember
#
# An object member.
#
# @name: the member's name, as defined in the QAPI schema.
#
# @type: the name of the member's type.
#
# @default: #optional default when used as command parameter.
# If absent, the parameter is mandatory.
# If present, the value must be null. The parameter is
# optional, and behavior when it's missing is not specified
# here.
# Future extension: if present and non-null, the parameter
# is optional, and defaults to this value.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoObjectMember',
'data': { 'name': 'str', 'type': 'str', '*default': 'any' } }
# @default's type must be null or match @type
##
# @SchemaInfoObjectVariant
#
# The variant members for a value of the type tag.
#
# @case: a value of the type tag.
#
# @type: the name of the object type that provides the variant members
# when the type tag has value @case.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoObjectVariant',
'data': { 'case': 'str', 'type': 'str' } }
##
# @SchemaInfoAlternate
#
# Additional SchemaInfo members for meta-type 'alternate'.
#
# @members: the alternate type's members.
# The members' wire encoding is distinct, see
# docs/qapi-code-gen.txt section Alternate types.
#
# On the wire, this can be any of the members.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoAlternate',
'data': { 'members': [ 'SchemaInfoAlternateMember' ] } }
##
# @SchemaInfoAlternateMember
#
# An alternate member.
#
# @type: the name of the member's type.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoAlternateMember',
'data': { 'type': 'str' } }
##
# @SchemaInfoCommand
#
# Additional SchemaInfo members for meta-type 'command'.
#
# @arg-type: the name of the object type that provides the command's
# parameters.
#
# @ret-type: the name of the command's result type.
#
# TODO @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
'data': { 'arg-type': 'str', 'ret-type': 'str' } }
##
# @SchemaInfoEvent
#
# Additional SchemaInfo members for meta-type 'event'.
#
# @arg-type: the name of the object type that provides the event's
# parameters.
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoEvent',
'data': { 'arg-type': 'str' } }
......@@ -151,6 +151,14 @@ static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
{
}
static void qapi_dealloc_type_anything(Visitor *v, QObject **obj,
const char *name, Error **errp)
{
if (obj) {
qobject_decref(*obj);
}
}
static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name,
Error **errp)
{
......@@ -216,6 +224,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
v->visitor.type_bool = qapi_dealloc_type_bool;
v->visitor.type_str = qapi_dealloc_type_str;
v->visitor.type_number = qapi_dealloc_type_number;
v->visitor.type_any = qapi_dealloc_type_anything;
v->visitor.type_size = qapi_dealloc_type_size;
v->visitor.start_union = qapi_dealloc_start_union;
......
......@@ -260,6 +260,12 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
v->type_number(v, obj, name, errp);
}
void visit_type_any(Visitor *v, QObject **obj, const char *name,
Error **errp)
{
v->type_any(v, obj, name, errp);
}
void output_type_enum(Visitor *v, int *obj, const char * const strings[],
const char *kind, const char *name,
Error **errp)
......
......@@ -286,6 +286,16 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
}
}
static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name,
Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
QObject *qobj = qmp_input_get_object(qiv, name, true);
qobject_incref(qobj);
*obj = qobj;
}
static void qmp_input_optional(Visitor *v, bool *present, const char *name,
Error **errp)
{
......@@ -329,6 +339,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
v->visitor.type_bool = qmp_input_type_bool;
v->visitor.type_str = qmp_input_type_str;
v->visitor.type_number = qmp_input_type_number;
v->visitor.type_any = qmp_input_type_any;
v->visitor.optional = qmp_input_optional;
v->visitor.get_next_type = qmp_input_get_next_type;
......
......@@ -66,9 +66,13 @@ static QObject *qmp_output_first(QmpOutputVisitor *qov)
{
QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
/* FIXME - find a better way to deal with NULL values */
/*
* FIXME Wrong, because qmp_output_get_qobject() will increment
* the refcnt *again*. We need to think through how visitors
* handle null.
*/
if (!e) {
return NULL;
return qnull();
}
return e->value;
......@@ -186,6 +190,14 @@ static void qmp_output_type_number(Visitor *v, double *obj, const char *name,
qmp_output_add(qov, name, qfloat_from_double(*obj));
}
static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name,
Error **errp)
{
QmpOutputVisitor *qov = to_qov(v);
qobject_incref(*obj);
qmp_output_add_obj(qov, name, *obj);
}
QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
{
QObject *obj = qmp_output_first(qov);
......@@ -233,6 +245,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void)
v->visitor.type_bool = qmp_output_type_bool;
v->visitor.type_str = qmp_output_type_str;
v->visitor.type_number = qmp_output_type_number;
v->visitor.type_any = qmp_output_type_any;
QTAILQ_INIT(&v->stack);
......
此差异已折叠。
......@@ -157,9 +157,9 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
* #ifdef CONFIG_SPICE. Necessary for an accurate query-commands
* result. However, the QAPI schema is blissfully unaware of that,
* and the QAPI code generator happily generates a dead
* qmp_marshal_input_query_spice() that calls qmp_query_spice().
* Provide it one, or else linking fails.
* FIXME Educate the QAPI schema on CONFIG_SPICE.
* qmp_marshal_query_spice() that calls qmp_query_spice(). Provide it
* one, or else linking fails. FIXME Educate the QAPI schema on
* CONFIG_SPICE.
*/
SpiceInfo *qmp_query_spice(Error **errp)
{
......@@ -234,12 +234,9 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
return props;
}
/* FIXME: teach qapi about how to pass through Visitors */
void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp)
void qmp_qom_set(const char *path, const char *property, QObject *value,
Error **errp)
{
const char *path = qdict_get_str(qdict, "path");
const char *property = qdict_get_str(qdict, "property");
QObject *value = qdict_get(qdict, "value");
Object *obj;
obj = object_resolve_path(path, NULL);
......@@ -252,20 +249,18 @@ void qmp_qom_set(QDict *qdict, QObject **ret, Error **errp)
object_property_set_qobject(obj, value, property, errp);
}
void qmp_qom_get(QDict *qdict, QObject **ret, Error **errp)
QObject *qmp_qom_get(const char *path, const char *property, Error **errp)
{
const char *path = qdict_get_str(qdict, "path");
const char *property = qdict_get_str(qdict, "property");
Object *obj;
obj = object_resolve_path(path, NULL);
if (!obj) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", path);
return;
return NULL;
}
*ret = object_property_get_qobject(obj, property, errp);
return object_property_get_qobject(obj, property, errp);
}
void qmp_set_password(const char *protocol, const char *password,
......@@ -661,11 +656,9 @@ out:
object_unref(obj);
}
void qmp_object_add(QDict *qdict, QObject **ret, Error **errp)
void qmp_object_add(const char *type, const char *id,
bool has_props, QObject *props, Error **errp)
{
const char *type = qdict_get_str(qdict, "qom-type");
const char *id = qdict_get_str(qdict, "id");
QObject *props = qdict_get(qdict, "props");
const QDict *pdict = NULL;
QmpInputVisitor *qiv;
......
......@@ -12,22 +12,18 @@
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from ordereddict import OrderedDict
from qapi import *
import re
def generate_command_decl(name, args, ret_type):
arglist=""
for argname, argtype, optional in parse_args(args):
argtype = c_type(argtype, is_param=True)
if optional:
arglist += "bool has_%s, " % c_name(argname)
arglist += "%s %s, " % (argtype, c_name(argname))
def gen_command_decl(name, arg_type, ret_type):
return mcgen('''
%(ret_type)s qmp_%(name)s(%(args)sError **errp);
%(c_type)s qmp_%(c_name)s(%(params)s);
''',
ret_type=c_type(ret_type), name=c_name(name),
args=arglist)
c_type=(ret_type and ret_type.c_type()) or 'void',
c_name=c_name(name),
params=gen_params(arg_type, 'Error **errp'))
def gen_err_check(err):
if not err:
......@@ -39,110 +35,124 @@ if (%(err)s) {
''',
err=err)
def gen_sync_call(name, args, ret_type):
ret = ""
arglist=""
retval=""
def gen_call(name, arg_type, ret_type):
ret = ''
argstr = ''
if arg_type:
for memb in arg_type.members:
if memb.optional:
argstr += 'has_%s, ' % c_name(memb.name)
argstr += '%s, ' % c_name(memb.name)
lhs = ''
if ret_type:
retval = "retval = "
for argname, argtype, optional in parse_args(args):
if optional:
arglist += "has_%s, " % c_name(argname)
arglist += "%s, " % (c_name(argname))
lhs = 'retval = '
push_indent()
ret = mcgen('''
%(retval)sqmp_%(name)s(%(args)s&local_err);
%(lhs)sqmp_%(c_name)s(%(args)s&local_err);
''',
name=c_name(name), args=arglist, retval=retval)
c_name=c_name(name), args=argstr, lhs=lhs)
if ret_type:
ret += gen_err_check('local_err')
ret += mcgen('''
qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
''',
c_name=c_name(name))
c_name=ret_type.c_name())
pop_indent()
return ret
def gen_visitor_input_containers_decl(args):
ret = ""
def gen_marshal_vars(arg_type, ret_type):
ret = mcgen('''
Error *local_err = NULL;
''')
push_indent()
if len(args) > 0:
if ret_type:
ret += mcgen('''
%(c_type)s retval;
''',
c_type=ret_type.c_type())
if arg_type:
ret += mcgen('''
QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
QapiDeallocVisitor *md;
Visitor *v;
''')
pop_indent()
return ret
def gen_visitor_input_vars_decl(args):
ret = ""
push_indent()
for argname, argtype, optional in parse_args(args):
if optional:
ret += mcgen('''
bool has_%(argname)s = false;
''',
argname=c_name(argname))
if is_c_ptr(argtype):
ret += mcgen('''
%(argtype)s %(argname)s = NULL;
for memb in arg_type.members:
if memb.optional:
ret += mcgen('''
bool has_%(c_name)s = false;
''',
argname=c_name(argname), argtype=c_type(argtype))
else:
c_name=c_name(memb.name))
ret += mcgen('''
%(argtype)s %(argname)s = {0};
%(c_type)s %(c_name)s = %(c_null)s;
''',
argname=c_name(argname), argtype=c_type(argtype))
c_name=c_name(memb.name),
c_type=memb.type.c_type(),
c_null=memb.type.c_null())
ret += '\n'
else:
ret += mcgen('''
(void)args;
''')
pop_indent()
return ret
def gen_visitor_input_block(args, dealloc=False):
ret = ""
errparg = '&local_err'
errarg = 'local_err'
if len(args) == 0:
def gen_marshal_input_visit(arg_type, dealloc=False):
ret = ''
if not arg_type:
return ret
push_indent()
if dealloc:
errparg = 'NULL'
errarg = None;
errarg = None
ret += mcgen('''
qmp_input_visitor_cleanup(mi);
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
''')
else:
errparg = '&local_err'
errarg = 'local_err'
ret += mcgen('''
v = qmp_input_get_visitor(mi);
''')
for argname, argtype, optional in parse_args(args):
if optional:
for memb in arg_type.members:
if memb.optional:
ret += mcgen('''
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
''',
c_name=c_name(argname), name=argname, errp=errparg)
c_name=c_name(memb.name), name=memb.name,
errp=errparg)
ret += gen_err_check(errarg)
ret += mcgen('''
if (has_%(c_name)s) {
''',
c_name=c_name(argname))
c_name=c_name(memb.name))
push_indent()
ret += mcgen('''
visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s);
''',
c_name=c_name(argname), name=argname, argtype=argtype,
visitor=type_name(argtype), errp=errparg)
c_name=c_name(memb.name), name=memb.name,
c_type=memb.type.c_name(), errp=errparg)
ret += gen_err_check(errarg)
if optional:
if memb.optional:
pop_indent()
ret += mcgen('''
}
......@@ -155,12 +165,11 @@ qapi_dealloc_visitor_cleanup(md);
pop_indent()
return ret
def gen_marshal_output(name, ret_type):
if not ret_type:
return ""
ret = mcgen('''
static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
def gen_marshal_output(ret_type):
return mcgen('''
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
{
Error *local_err = NULL;
QmpOutputVisitor *mo = qmp_output_visitor_new();
......@@ -168,7 +177,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o
Visitor *v;
v = qmp_output_get_visitor(mo);
visit_type_%(visitor)s(v, &ret_in, "unused", &local_err);
visit_type_%(c_name)s(v, &ret_in, "unused", &local_err);
if (local_err) {
goto out;
}
......@@ -179,51 +188,40 @@ out:
qmp_output_visitor_cleanup(mo);
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
visit_type_%(visitor)s(v, &ret_in, "unused", NULL);
visit_type_%(c_name)s(v, &ret_in, "unused", NULL);
qapi_dealloc_visitor_cleanup(md);
}
''',
c_ret_type=c_type(ret_type), c_name=c_name(name),
visitor=type_name(ret_type))
c_type=ret_type.c_type(), c_name=ret_type.c_name())
return ret
def gen_marshal_input_decl(name, middle_mode):
ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
def gen_marshal_proto(name):
ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
if not middle_mode:
ret = "static " + ret
ret = 'static ' + ret
return ret
def gen_marshal_input(name, args, ret_type, middle_mode):
hdr = gen_marshal_input_decl(name, middle_mode)
ret = mcgen('''
%(header)s
{
Error *local_err = NULL;
''',
header=hdr)
if ret_type:
ret += mcgen('''
%(c_type)s retval;
def gen_marshal_decl(name):
return mcgen('''
%(proto)s;
''',
c_type=c_type(ret_type))
proto=gen_marshal_proto(name))
if len(args) > 0:
ret += gen_visitor_input_containers_decl(args)
ret += gen_visitor_input_vars_decl(args) + '\n'
ret += gen_visitor_input_block(args) + '\n'
else:
ret += mcgen('''
(void)args;
def gen_marshal(name, arg_type, ret_type):
ret = mcgen('''
''')
%(proto)s
{
''',
proto=gen_marshal_proto(name))
ret += gen_sync_call(name, args, ret_type)
ret += gen_marshal_vars(arg_type, ret_type)
ret += gen_marshal_input_visit(arg_type)
ret += gen_call(name, arg_type, ret_type)
if re.search('^ *goto out\\;', ret, re.MULTILINE):
if re.search('^ *goto out;', ret, re.MULTILINE):
ret += mcgen('''
out:
......@@ -231,27 +229,31 @@ out:
ret += mcgen('''
error_propagate(errp, local_err);
''')
ret += gen_visitor_input_block(args, dealloc=True)
ret += gen_marshal_input_visit(arg_type, dealloc=True)
ret += mcgen('''
}
''')
return ret
def gen_registry(commands):
registry=""
def gen_register_command(name, success_response):
push_indent()
for cmd in commands:
options = 'QCO_NO_OPTIONS'
if not cmd.get('success-response', True):
options = 'QCO_NO_SUCCESS_RESP'
options = 'QCO_NO_OPTIONS'
if not success_response:
options = 'QCO_NO_SUCCESS_RESP'
registry += mcgen('''
qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
ret = mcgen('''
qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
''',
name=cmd['command'], c_name=c_name(cmd['command']),
opts=options)
name=name, c_name=c_name(name),
opts=options)
pop_indent()
return ret
def gen_registry(registry):
ret = mcgen('''
static void qmp_init_marshal(void)
{
''')
......@@ -263,6 +265,41 @@ qapi_init(qmp_init_marshal);
''')
return ret
class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
def __init__(self):
self.decl = None
self.defn = None
self._regy = None
self._visited_ret_types = None
def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._regy = ''
self._visited_ret_types = set()
def visit_end(self):
if not middle_mode:
self.defn += gen_registry(self._regy)
self._regy = None
self._visited_ret_types = None
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response):
if not gen:
return
self.decl += gen_command_decl(name, arg_type, ret_type)
if ret_type and ret_type not in self._visited_ret_types:
self._visited_ret_types.add(ret_type)
self.defn += gen_marshal_output(ret_type)
if middle_mode:
self.decl += gen_marshal_decl(name)
self.defn += gen_marshal(name, arg_type, ret_type)
if not middle_mode:
self._regy += gen_register_command(name, success_response)
middle_mode = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
......@@ -272,10 +309,6 @@ for o, a in opts:
if o in ("-m", "--middle"):
middle_mode = True
exprs = parse_schema(input_file)
commands = filter(lambda expr: expr.has_key('command'), exprs)
commands = filter(lambda expr: not expr.has_key('gen'), commands)
c_comment = '''
/*
* schema-defined QMP->QAPI command dispatch
......@@ -323,7 +356,7 @@ fdef.write(mcgen('''
#include "%(prefix)sqmp-commands.h"
''',
prefix=prefix))
prefix=prefix))
fdecl.write(mcgen('''
#include "%(prefix)sqapi-types.h"
......@@ -331,29 +364,12 @@ fdecl.write(mcgen('''
#include "qapi/error.h"
''',
prefix=prefix))
for cmd in commands:
arglist = []
ret_type = None
if cmd.has_key('data'):
arglist = cmd['data']
if cmd.has_key('returns'):
ret_type = cmd['returns']
ret = generate_command_decl(cmd['command'], arglist, ret_type)
fdecl.write(ret)
if ret_type:
ret = gen_marshal_output(cmd['command'], ret_type) + "\n"
fdef.write(ret)
if middle_mode:
fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode))
ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
fdef.write(ret)
prefix=prefix))
if not middle_mode:
ret = gen_registry(commands)
fdef.write(ret)
schema = QAPISchema(input_file)
gen = QAPISchemaGenCommandVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
close_output(fdef, fdecl)
......@@ -2,78 +2,64 @@
# QAPI event generator
#
# Copyright (c) 2014 Wenchao Xia
# Copyright (c) 2015 Red Hat Inc.
#
# Authors:
# Wenchao Xia <wenchaoqemu@gmail.com>
# Markus Armbruster <armbru@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from ordereddict import OrderedDict
from qapi import *
def _generate_event_api_name(event_name, params):
api_name = "void qapi_event_send_%s(" % c_name(event_name).lower();
l = len(api_name)
if params:
for argname, argentry, optional in parse_args(params):
if optional:
api_name += "bool has_%s,\n" % c_name(argname)
api_name += "".ljust(l)
def gen_event_send_proto(name, arg_type):
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
'c_name': c_name(name.lower()),
'param': gen_params(arg_type, 'Error **errp')}
api_name += "%s %s,\n" % (c_type(argentry, is_param=True),
c_name(argname))
api_name += "".ljust(l)
api_name += "Error **errp)"
return api_name;
# Following are the core functions that generate C APIs to emit event.
def generate_event_declaration(api_name):
def gen_event_send_decl(name, arg_type):
return mcgen('''
%(api_name)s;
%(proto)s;
''',
api_name = api_name)
proto=gen_event_send_proto(name, arg_type))
def generate_event_implement(api_name, event_name, params):
# step 1: declare any variables
ret = mcgen("""
%(api_name)s
def gen_event_send(name, arg_type):
ret = mcgen('''
%(proto)s
{
QDict *qmp;
Error *local_err = NULL;
QMPEventFuncEmit emit;
""",
api_name = api_name)
''',
proto=gen_event_send_proto(name, arg_type))
if params:
ret += mcgen("""
if arg_type and arg_type.members:
ret += mcgen('''
QmpOutputVisitor *qov;
Visitor *v;
QObject *obj;
""")
''')
# step 2: check emit function, create a dict
ret += mcgen("""
ret += mcgen('''
emit = qmp_event_get_func_emit();
if (!emit) {
return;
}
qmp = qmp_event_build_dict("%(event_name)s");
qmp = qmp_event_build_dict("%(name)s");
""",
event_name = event_name)
''',
name=name)
# step 3: visit the params if params != None
if params:
ret += mcgen("""
if arg_type and arg_type.members:
ret += mcgen('''
qov = qmp_output_visitor_new();
g_assert(qov);
......@@ -81,45 +67,46 @@ def generate_event_implement(api_name, event_name, params):
g_assert(v);
/* Fake visit, as if all members are under a structure */
visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err);
visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err);
if (local_err) {
goto clean;
}
""",
event_name = event_name)
''',
name=name)
for argname, argentry, optional in parse_args(params):
if optional:
ret += mcgen("""
if (has_%(var)s) {
""",
var = c_name(argname))
for memb in arg_type.members:
if memb.optional:
ret += mcgen('''
if (has_%(c_name)s) {
''',
c_name=c_name(memb.name))
push_indent()
if argentry == "str":
var_type = "(char **)"
# Ugly: need to cast away the const
if memb.type.name == "str":
cast = '(char **)'
else:
var_type = ""
cast = ''
ret += mcgen("""
visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err);
ret += mcgen('''
visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err);
if (local_err) {
goto clean;
}
""",
var_type = var_type,
var = c_name(argname),
type = type_name(argentry),
name = argname)
''',
cast=cast,
c_name=c_name(memb.name),
c_type=memb.type.c_name(),
name=memb.name)
if optional:
if memb.optional:
pop_indent()
ret += mcgen("""
ret += mcgen('''
}
""")
''')
ret += mcgen("""
ret += mcgen('''
visit_end_struct(v, &local_err);
if (local_err) {
......@@ -130,85 +117,48 @@ def generate_event_implement(api_name, event_name, params):
g_assert(obj != NULL);
qdict_put_obj(qmp, "data", obj);
""")
''')
# step 4: call qmp event api
ret += mcgen("""
emit(%(event_enum_value)s, qmp, &local_err);
ret += mcgen('''
emit(%(c_enum)s, qmp, &local_err);
""",
event_enum_value = event_enum_value)
''',
c_enum=c_enum_const(event_enum_name, name))
# step 5: clean up
if params:
ret += mcgen("""
if arg_type and arg_type.members:
ret += mcgen('''
clean:
qmp_output_visitor_cleanup(qov);
""")
ret += mcgen("""
''')
ret += mcgen('''
error_propagate(errp, local_err);
QDECREF(qmp);
}
""")
''')
return ret
# Following are the functions that generate an enum type for all defined
# events, similar to qapi-types.py. Here we already have enum name and
# values which were generated before and recorded in event_enum_*. It also
# works around the issue that "import qapi-types" can't work.
def generate_event_enum_decl(event_enum_name, event_enum_values):
lookup_decl = mcgen('''
extern const char *%(event_enum_name)s_lookup[];
''',
event_enum_name = event_enum_name)
enum_decl = mcgen('''
typedef enum %(event_enum_name)s {
''',
event_enum_name = event_enum_name)
# append automatically generated _MAX value
enum_max_value = c_enum_const(event_enum_name, "MAX")
enum_values = event_enum_values + [ enum_max_value ]
i = 0
for value in enum_values:
enum_decl += mcgen('''
%(value)s = %(i)d,
''',
value = value,
i = i)
i += 1
enum_decl += mcgen('''
} %(event_enum_name)s;
''',
event_enum_name = event_enum_name)
return lookup_decl + enum_decl
class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
def __init__(self):
self.decl = None
self.defn = None
self._event_names = None
def generate_event_enum_lookup(event_enum_name, event_enum_strings):
ret = mcgen('''
def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._event_names = []
const char *%(event_enum_name)s_lookup[] = {
''',
event_enum_name = event_enum_name)
def visit_end(self):
self.decl += gen_enum(event_enum_name, self._event_names)
self.defn += gen_enum_lookup(event_enum_name, self._event_names)
self._event_names = None
for string in event_enum_strings:
ret += mcgen('''
"%(string)s",
''',
string = string)
def visit_event(self, name, info, arg_type):
self.decl += gen_event_send_decl(name, arg_type)
self.defn += gen_event_send(name, arg_type)
self._event_names.append(name)
ret += mcgen('''
NULL,
};
''')
return ret
(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
......@@ -263,35 +213,12 @@ fdecl.write(mcgen('''
''',
prefix=prefix))
exprs = parse_schema(input_file)
event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
event_enum_values = []
event_enum_strings = []
for expr in exprs:
if expr.has_key('event'):
event_name = expr['event']
params = expr.get('data')
if params and len(params) == 0:
params = None
api_name = _generate_event_api_name(event_name, params)
ret = generate_event_declaration(api_name)
fdecl.write(ret)
# We need an enum value per event
event_enum_value = c_enum_const(event_enum_name, event_name)
ret = generate_event_implement(api_name, event_name, params)
fdef.write(ret)
# Record it, and generate enum later
event_enum_values.append(event_enum_value)
event_enum_strings.append(event_name)
ret = generate_event_enum_decl(event_enum_name, event_enum_values)
fdecl.write(ret)
ret = generate_event_enum_lookup(event_enum_name, event_enum_strings)
fdef.write(ret)
schema = QAPISchema(input_file)
gen = QAPISchemaGenEventVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
close_output(fdef, fdecl)
#
# QAPI introspection generator
#
# Copyright (C) 2015 Red Hat, Inc.
#
# Authors:
# Markus Armbruster <armbru@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from qapi import *
# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
# TODO try to use json.dumps() once we get unstuck
def to_json(obj, level=0):
if obj is None:
ret = 'null'
elif isinstance(obj, str):
ret = '"' + obj.replace('"', r'\"') + '"'
elif isinstance(obj, list):
elts = [to_json(elt, level + 1)
for elt in obj]
ret = '[' + ', '.join(elts) + ']'
elif isinstance(obj, dict):
elts = ['"%s": %s' % (key.replace('"', r'\"'),
to_json(obj[key], level + 1))
for key in sorted(obj.keys())]
ret = '{' + ', '.join(elts) + '}'
else:
assert False # not implemented
if level == 1:
ret = '\n' + ret
return ret
def to_c_string(string):
return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
def __init__(self, unmask):
self._unmask = unmask
self.defn = None
self.decl = None
self._schema = None
self._jsons = None
self._used_types = None
self._name_map = None
def visit_begin(self, schema):
self._schema = schema
self._jsons = []
self._used_types = []
self._name_map = {}
return QAPISchemaType # don't visit types for now
def visit_end(self):
# visit the types that are actually used
jsons = self._jsons
self._jsons = []
for typ in self._used_types:
typ.visit(self)
# generate C
# TODO can generate awfully long lines
jsons.extend(self._jsons)
name = prefix + 'qmp_schema_json'
self.decl = mcgen('''
extern const char %(c_name)s[];
''',
c_name=c_name(name))
lines = to_json(jsons).split('\n')
c_string = '\n '.join([to_c_string(line) for line in lines])
self.defn = mcgen('''
const char %(c_name)s[] = %(c_string)s;
''',
c_name=c_name(name),
c_string=c_string)
self._schema = None
self._jsons = None
self._used_types = None
self._name_map = None
def _name(self, name):
if self._unmask:
return name
if name not in self._name_map:
self._name_map[name] = '%d' % len(self._name_map)
return self._name_map[name]
def _use_type(self, typ):
# Map the various integer types to plain int
if typ.json_type() == 'int':
typ = self._schema.lookup_type('int')
elif (isinstance(typ, QAPISchemaArrayType) and
typ.element_type.json_type() == 'int'):
typ = self._schema.lookup_type('intList')
# Add type to work queue if new
if typ not in self._used_types:
self._used_types.append(typ)
# Clients should examine commands and events, not types. Hide
# type names to reduce the temptation. Also saves a few
# characters.
if isinstance(typ, QAPISchemaBuiltinType):
return typ.name
return self._name(typ.name)
def _gen_json(self, name, mtype, obj):
if mtype != 'command' and mtype != 'event' and mtype != 'builtin':
name = self._name(name)
obj['name'] = name
obj['meta-type'] = mtype
self._jsons.append(obj)
def _gen_member(self, member):
ret = {'name': member.name, 'type': self._use_type(member.type)}
if member.optional:
ret['default'] = None
return ret
def _gen_variants(self, tag_name, variants):
return {'tag': tag_name,
'variants': [self._gen_variant(v) for v in variants]}
def _gen_variant(self, variant):
return {'case': variant.name, 'type': self._use_type(variant.type)}
def visit_builtin_type(self, name, info, json_type):
self._gen_json(name, 'builtin', {'json-type': json_type})
def visit_enum_type(self, name, info, values, prefix):
self._gen_json(name, 'enum', {'values': values})
def visit_array_type(self, name, info, element_type):
self._gen_json(name, 'array',
{'element-type': self._use_type(element_type)})
def visit_object_type_flat(self, name, info, members, variants):
obj = {'members': [self._gen_member(m) for m in members]}
if variants:
obj.update(self._gen_variants(variants.tag_member.name,
variants.variants))
self._gen_json(name, 'object', obj)
def visit_alternate_type(self, name, info, variants):
self._gen_json(name, 'alternate',
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]})
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
self._gen_json(name, 'command',
{'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type)})
def visit_event(self, name, info, arg_type):
arg_type = arg_type or self._schema.the_empty_object_type
self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
# Debugging aid: unmask QAPI schema's type names
# We normally mask them, because they're not QMP wire ABI
opt_unmask = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
parse_command_line("u", ["unmask-non-abi-names"])
for o, a in opts:
if o in ("-u", "--unmask-non-abi-names"):
opt_unmask = True
c_comment = '''
/*
* QAPI/QMP schema introspection
*
* Copyright (C) 2015 Red Hat, Inc.
*
* 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.
*
*/
'''
h_comment = '''
/*
* QAPI/QMP schema introspection
*
* Copyright (C) 2015 Red Hat, Inc.
*
* 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.
*
*/
'''
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
'qmp-introspect.c', 'qmp-introspect.h',
c_comment, h_comment)
fdef.write(mcgen('''
#include "%(prefix)sqmp-introspect.h"
''',
prefix=prefix))
schema = QAPISchema(input_file)
gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
close_output(fdef, fdecl)
......@@ -2,96 +2,81 @@
# QAPI types generator
#
# Copyright IBM, Corp. 2011
# Copyright (c) 2013-2015 Red Hat Inc.
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
# Markus Armbruster <armbru@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from ordereddict import OrderedDict
from qapi import *
def generate_fwd_builtin(name):
return mcgen('''
typedef struct %(name)sList {
union {
%(type)s value;
uint64_t padding;
};
struct %(name)sList *next;
} %(name)sList;
''',
type=c_type(name),
name=name)
def generate_fwd_struct(name):
def gen_fwd_object_or_array(name):
return mcgen('''
typedef struct %(name)s %(name)s;
typedef struct %(name)sList {
union {
%(name)s *value;
uint64_t padding;
};
struct %(name)sList *next;
} %(name)sList;
typedef struct %(c_name)s %(c_name)s;
''',
name=c_name(name))
c_name=c_name(name))
def generate_fwd_enum_struct(name):
def gen_array(name, element_type):
return mcgen('''
typedef struct %(name)sList {
struct %(c_name)s {
union {
%(name)s value;
%(c_type)s value;
uint64_t padding;
};
struct %(name)sList *next;
} %(name)sList;
%(c_name)s *next;
};
''',
name=c_name(name))
c_name=c_name(name), c_type=element_type.c_type())
def generate_struct_fields(members):
def gen_struct_field(name, typ, optional):
ret = ''
for argname, argentry, optional in parse_args(members):
if optional:
ret += mcgen('''
if optional:
ret += mcgen('''
bool has_%(c_name)s;
''',
c_name=c_name(argname))
ret += mcgen('''
c_name=c_name(name))
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=c_type(argentry), c_name=c_name(argname))
c_type=typ.c_type(), c_name=c_name(name))
return ret
def generate_struct(expr):
structname = expr.get('struct', "")
members = expr['data']
base = expr.get('base')
def gen_struct_fields(members):
ret = ''
for memb in members:
ret += gen_struct_field(memb.name, memb.type, memb.optional)
return ret
def gen_struct(name, base, members):
ret = mcgen('''
struct %(name)s {
struct %(c_name)s {
''',
name=c_name(structname))
c_name=c_name(name))
if base:
ret += generate_struct_fields({'base': base})
ret += gen_struct_field('base', base, False)
ret += generate_struct_fields(members)
ret += gen_struct_fields(members)
# Make sure that all structs have at least one field; this avoids
# potential issues with attempting to malloc space for zero-length structs
# in C, and also incompatibility with C++ (where an empty struct is size 1).
# potential issues with attempting to malloc space for zero-length
# structs in C, and also incompatibility with C++ (where an empty
# struct is size 1).
if not base and not members:
ret += mcgen('''
ret += mcgen('''
char qapi_dummy_field_for_empty_struct;
''')
......@@ -101,81 +86,32 @@ struct %(name)s {
return ret
def generate_enum_lookup(name, values, prefix=None):
ret = mcgen('''
const char *const %(name)s_lookup[] = {
''',
name=c_name(name))
for value in values:
index = c_enum_const(name, value, prefix)
ret += mcgen('''
[%(index)s] = "%(value)s",
''',
index = index, value = value)
max_index = c_enum_const(name, 'MAX', prefix)
ret += mcgen('''
[%(max_index)s] = NULL,
};
''',
max_index=max_index)
return ret
def generate_enum(name, values, prefix=None):
name = c_name(name)
lookup_decl = mcgen('''
extern const char *const %(name)s_lookup[];
''',
name=name)
enum_decl = mcgen('''
typedef enum %(name)s {
''',
name=name)
# append automatically generated _MAX value
enum_values = values + [ 'MAX' ]
i = 0
for value in enum_values:
enum_full_value = c_enum_const(name, value, prefix)
enum_decl += mcgen('''
%(enum_full_value)s = %(i)d,
''',
enum_full_value = enum_full_value,
i=i)
i += 1
def gen_alternate_qtypes_decl(name):
return mcgen('''
enum_decl += mcgen('''
} %(name)s;
extern const int %(c_name)s_qtypes[];
''',
name=name)
c_name=c_name(name))
return enum_decl + lookup_decl
def generate_alternate_qtypes(expr):
name = expr['alternate']
members = expr['data']
def gen_alternate_qtypes(name, variants):
ret = mcgen('''
const int %(name)s_qtypes[QTYPE_MAX] = {
const int %(c_name)s_qtypes[QTYPE_MAX] = {
''',
name=c_name(name))
c_name=c_name(name))
for key in members:
qtype = find_alternate_member_qtype(members[key])
assert qtype, "Invalid alternate member"
for var in variants.variants:
qtype = var.type.alternate_qtype()
assert qtype
ret += mcgen('''
[%(qtype)s] = %(enum_const)s,
''',
qtype = qtype,
enum_const = c_enum_const(name + 'Kind', key))
qtype=qtype,
enum_const=c_enum_const(variants.tag_member.type.name,
var.name))
ret += mcgen('''
};
......@@ -183,41 +119,26 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
return ret
def generate_union(expr, meta):
name = c_name(expr[meta])
typeinfo = expr['data']
base = expr.get('base')
discriminator = expr.get('discriminator')
enum_define = discriminator_find_enum_define(expr)
if enum_define:
discriminator_type_name = enum_define['enum_name']
else:
discriminator_type_name = '%sKind' % (name)
def gen_union(name, base, variants):
ret = mcgen('''
struct %(name)s {
struct %(c_name)s {
''',
name=name)
c_name=c_name(name))
if base:
ret += mcgen('''
/* Members inherited from %(c_name)s: */
''',
c_name=c_name(base))
base_fields = find_struct(base)['data']
ret += generate_struct_fields(base_fields)
c_name=c_name(base.name))
ret += gen_struct_fields(base.members)
ret += mcgen('''
/* Own members: */
''')
else:
assert not discriminator
ret += mcgen('''
%(discriminator_type_name)s kind;
%(c_type)s kind;
''',
discriminator_type_name=c_name(discriminator_type_name))
c_type=c_name(variants.tag_member.type.name))
# FIXME: What purpose does data serve, besides preventing a union that
# has a branch named 'data'? We use it in qapi-visit.py to decide
......@@ -231,39 +152,41 @@ struct %(name)s {
union { /* union tag is @%(c_name)s */
void *data;
''',
c_name=c_name(discriminator or 'kind'))
for key in typeinfo:
# TODO ugly special case for simple union
# Use same tag name in C as on the wire to get rid of
# it, then: c_name=c_name(variants.tag_member.name)
c_name=c_name(variants.tag_name or 'kind'))
for var in variants.variants:
# Ugly special case for simple union TODO get rid of it
typ = var.simple_union_type() or var.type
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=c_type(typeinfo[key]),
c_name=c_name(key))
c_type=typ.c_type(),
c_name=c_name(var.name))
ret += mcgen('''
};
};
''')
if meta == 'alternate':
ret += mcgen('''
extern const int %(name)s_qtypes[];
''',
name=name)
return ret
def generate_type_cleanup_decl(name):
def gen_type_cleanup_decl(name):
ret = mcgen('''
void qapi_free_%(name)s(%(c_type)s obj);
void qapi_free_%(c_name)s(%(c_name)s *obj);
''',
c_type=c_type(name), name=c_name(name))
c_name=c_name(name))
return ret
def generate_type_cleanup(name):
def gen_type_cleanup(name):
ret = mcgen('''
void qapi_free_%(name)s(%(c_type)s obj)
void qapi_free_%(c_name)s(%(c_name)s *obj)
{
QapiDeallocVisitor *md;
Visitor *v;
......@@ -274,13 +197,83 @@ void qapi_free_%(name)s(%(c_type)s obj)
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
visit_type_%(name)s(v, &obj, NULL, NULL);
visit_type_%(c_name)s(v, &obj, NULL, NULL);
qapi_dealloc_visitor_cleanup(md);
}
''',
c_type=c_type(name), name=c_name(name))
c_name=c_name(name))
return ret
class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
def __init__(self):
self.decl = None
self.defn = None
self._fwdecl = None
self._fwdefn = None
self._btin = None
def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._fwdecl = ''
self._fwdefn = ''
self._btin = guardstart('QAPI_TYPES_BUILTIN')
def visit_end(self):
self.decl = self._fwdecl + self.decl
self._fwdecl = None
self.defn = self._fwdefn + self.defn
self._fwdefn = None
# To avoid header dependency hell, we always generate
# declarations for built-in types in our header files and
# simply guard them. See also do_builtins (command line
# option -b).
self._btin += guardend('QAPI_TYPES_BUILTIN')
self.decl = self._btin + self.decl
self._btin = None
def _gen_type_cleanup(self, name):
self.decl += gen_type_cleanup_decl(name)
self.defn += gen_type_cleanup(name)
def visit_enum_type(self, name, info, values, prefix):
self._fwdecl += gen_enum(name, values, prefix)
self._fwdefn += gen_enum_lookup(name, values, prefix)
def visit_array_type(self, name, info, element_type):
if isinstance(element_type, QAPISchemaBuiltinType):
self._btin += gen_fwd_object_or_array(name)
self._btin += gen_array(name, element_type)
self._btin += gen_type_cleanup_decl(name)
if do_builtins:
self.defn += gen_type_cleanup(name)
else:
self._fwdecl += gen_fwd_object_or_array(name)
self.decl += gen_array(name, element_type)
self._gen_type_cleanup(name)
def visit_object_type(self, name, info, base, members, variants):
if info:
self._fwdecl += gen_fwd_object_or_array(name)
if variants:
assert not members # not implemented
self.decl += gen_union(name, base, variants)
else:
self.decl += gen_struct(name, base, members)
self._gen_type_cleanup(name)
def visit_alternate_type(self, name, info, variants):
self._fwdecl += gen_fwd_object_or_array(name)
self._fwdefn += gen_alternate_qtypes(name, variants)
self.decl += gen_union(name, None, variants)
self.decl += gen_alternate_qtypes_decl(name)
self._gen_type_cleanup(name)
# If you link code generated from multiple schemata, you want only one
# instance of the code for built-in types. Generate it only when
# do_builtins, enabled by command line option -b. See also
# QAPISchemaGenTypeVisitor.visit_end().
do_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
......@@ -334,81 +327,13 @@ fdef.write(mcgen('''
fdecl.write(mcgen('''
#include <stdbool.h>
#include <stdint.h>
#include "qapi/qmp/qobject.h"
'''))
exprs = parse_schema(input_file)
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
for typename in builtin_types.keys():
fdecl.write(generate_fwd_builtin(typename))
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
for expr in exprs:
ret = ""
if expr.has_key('struct'):
ret += generate_fwd_struct(expr['struct'])
elif expr.has_key('enum'):
ret += generate_enum(expr['enum'], expr['data'],
expr.get('prefix'))
ret += generate_fwd_enum_struct(expr['enum'])
fdef.write(generate_enum_lookup(expr['enum'], expr['data'],
expr.get('prefix')))
elif expr.has_key('union'):
ret += generate_fwd_struct(expr['union'])
enum_define = discriminator_find_enum_define(expr)
if not enum_define:
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
expr['data'].keys()))
elif expr.has_key('alternate'):
ret += generate_fwd_struct(expr['alternate'])
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)
# 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.keys():
fdecl.write(generate_type_cleanup_decl(typename + "List"))
fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
# ...this doesn't work for cases where we link in multiple objects that
# have the functions defined, so we use -b option to provide control
# over these cases
if do_builtins:
for typename in builtin_types.keys():
fdef.write(generate_type_cleanup(typename + "List"))
for expr in exprs:
ret = ""
if expr.has_key('struct'):
ret += generate_struct(expr) + "\n"
ret += generate_type_cleanup_decl(expr['struct'] + "List")
fdef.write(generate_type_cleanup(expr['struct'] + "List"))
ret += generate_type_cleanup_decl(expr['struct'])
fdef.write(generate_type_cleanup(expr['struct']))
elif expr.has_key('union'):
ret += generate_union(expr, 'union') + "\n"
ret += generate_type_cleanup_decl(expr['union'] + "List")
fdef.write(generate_type_cleanup(expr['union'] + "List"))
ret += generate_type_cleanup_decl(expr['union'])
fdef.write(generate_type_cleanup(expr['union']))
elif expr.has_key('alternate'):
ret += generate_union(expr, 'alternate') + "\n"
ret += generate_type_cleanup_decl(expr['alternate'] + "List")
fdef.write(generate_type_cleanup(expr['alternate'] + "List"))
ret += generate_type_cleanup_decl(expr['alternate'])
fdef.write(generate_type_cleanup(expr['alternate']))
elif expr.has_key('enum'):
ret += "\n" + generate_type_cleanup_decl(expr['enum'] + "List")
fdef.write(generate_type_cleanup(expr['enum'] + "List"))
else:
continue
fdecl.write(ret)
schema = QAPISchema(input_file)
gen = QAPISchemaGenTypeVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
close_output(fdef, fdecl)
......@@ -12,25 +12,36 @@
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from ordereddict import OrderedDict
from qapi import *
import re
implicit_structs_seen = set()
struct_fields_seen = set()
def generate_visit_implicit_struct(type):
if type in implicit_structs_seen:
def gen_visit_decl(name, scalar=False):
c_type = c_name(name) + ' *'
if not scalar:
c_type += '*'
return mcgen('''
void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
''',
c_name=c_name(name), c_type=c_type)
def gen_visit_implicit_struct(typ):
if typ in implicit_structs_seen:
return ''
implicit_structs_seen.add(type)
implicit_structs_seen.add(typ)
ret = ''
if type not in struct_fields_seen:
if typ.name not in struct_fields_seen:
# Need a forward declaration
ret += mcgen('''
static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
''',
c_type=type_name(type))
c_type=typ.c_name())
ret += mcgen('''
......@@ -46,52 +57,53 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
error_propagate(errp, err);
}
''',
c_type=type_name(type))
c_type=typ.c_name())
return ret
def generate_visit_struct_fields(name, members, base = None):
def gen_visit_struct_fields(name, base, members):
struct_fields_seen.add(name)
ret = ''
if base:
ret += generate_visit_implicit_struct(base)
ret += gen_visit_implicit_struct(base)
ret += mcgen('''
static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
static void visit_type_%(c_name)s_fields(Visitor *m, %(c_name)s **obj, Error **errp)
{
Error *err = NULL;
''',
name=c_name(name))
c_name=c_name(name))
push_indent()
if base:
ret += mcgen('''
visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);
if (err) {
goto out;
}
''',
type=type_name(base), c_name=c_name('base'))
c_type=base.c_name(), c_name=c_name('base'))
for argname, argentry, optional in parse_args(members):
if optional:
for memb in members:
if memb.optional:
ret += mcgen('''
visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
if (!err && (*obj)->has_%(c_name)s) {
''',
c_name=c_name(argname), name=argname)
c_name=c_name(memb.name), name=memb.name)
push_indent()
ret += mcgen('''
visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
''',
type=type_name(argentry), c_name=c_name(argname),
name=argname)
c_type=memb.type.c_name(), c_name=c_name(memb.name),
name=memb.name)
if optional:
if memb.optional:
pop_indent()
ret += mcgen('''
}
......@@ -103,7 +115,7 @@ if (err) {
''')
pop_indent()
if re.search('^ *goto out\\;', ret, re.MULTILINE):
if re.search('^ *goto out;', ret, re.MULTILINE):
ret += mcgen('''
out:
......@@ -115,12 +127,17 @@ out:
return ret
def generate_visit_struct_body(name):
def gen_visit_struct(name, base, members):
ret = gen_visit_struct_fields(name, base, members)
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
# *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
# rather than leaving it non-NULL. As currently written, the caller must
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
ret = mcgen('''
ret += mcgen('''
void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
{
Error *err = NULL;
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
......@@ -131,37 +148,17 @@ def generate_visit_struct_body(name):
visit_end_struct(m, &err);
}
error_propagate(errp, err);
}
''',
name=name, c_name=c_name(name))
name=name, c_name=c_name(name))
return ret
def generate_visit_struct(expr):
name = expr['struct']
members = expr['data']
base = expr.get('base')
ret = generate_visit_struct_fields(name, members, base)
ret += mcgen('''
void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
{
''',
name=c_name(name))
ret += generate_visit_struct_body(name)
ret += mcgen('''
}
''')
return ret
def generate_visit_list(name):
def gen_visit_list(name, element_type):
return mcgen('''
void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
{
Error *err = NULL;
GenericList *i, **prev;
......@@ -174,8 +171,8 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
for (prev = (GenericList **)obj;
!err && (i = visit_next_list(m, prev, &err)) != NULL;
prev = &i) {
%(name)sList *native_i = (%(name)sList *)i;
visit_type_%(name)s(m, &native_i->value, NULL, &err);
%(c_name)s *native_i = (%(c_name)s *)i;
visit_type_%(c_elt_type)s(m, &native_i->value, NULL, &err);
}
error_propagate(errp, err);
......@@ -185,9 +182,10 @@ out:
error_propagate(errp, err);
}
''',
name=type_name(name))
c_name=c_name(name), c_elt_type=element_type.c_name())
def generate_visit_enum(name):
def gen_visit_enum(name):
return mcgen('''
void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
......@@ -197,44 +195,36 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
''',
c_name=c_name(name), name=name)
def generate_visit_alternate(name, members):
def gen_visit_alternate(name, variants):
ret = mcgen('''
void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
{
Error *err = NULL;
visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
visit_start_implicit_struct(m, (void**) obj, sizeof(%(c_name)s), &err);
if (err) {
goto out;
}
visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
visit_get_next_type(m, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
if (err) {
goto out_end;
}
switch ((*obj)->kind) {
''',
name=c_name(name))
# For alternate, always use the default enum type automatically generated
# as name + 'Kind'
disc_type = c_name(name) + 'Kind'
for key in members:
assert (members[key] in builtin_types.keys()
or find_struct(members[key])
or find_union(members[key])
or find_enum(members[key])), "Invalid alternate member"
c_name=c_name(name))
enum_full_value = c_enum_const(disc_type, key)
for var in variants.variants:
ret += mcgen('''
case %(enum_full_value)s:
case %(case)s:
visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
break;
''',
enum_full_value = enum_full_value,
c_type = type_name(members[key]),
c_name = c_name(key))
case=c_enum_const(variants.tag_member.type.name,
var.name),
c_type=var.type.c_name(),
c_name=c_name(var.name))
ret += mcgen('''
default:
......@@ -252,34 +242,17 @@ out:
return ret
def generate_visit_union(expr):
name = expr['union']
members = expr['data']
base = expr.get('base')
discriminator = expr.get('discriminator')
enum_define = discriminator_find_enum_define(expr)
if enum_define:
# Use the enum type as discriminator
ret = ""
disc_type = c_name(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
ret = generate_visit_enum(name + 'Kind')
disc_type = c_name(name) + 'Kind'
def gen_visit_union(name, base, variants):
ret = ''
if base:
assert discriminator
base_fields = find_struct(base)['data'].copy()
del base_fields[discriminator]
ret += generate_visit_struct_fields(name, base_fields)
members = [m for m in base.members if m != variants.tag_member]
ret += gen_visit_struct_fields(name, None, members)
if discriminator:
for key in members:
ret += generate_visit_implicit_struct(members[key])
for var in variants.variants:
# Ugly special case for simple union TODO get rid of it
if not var.simple_union_type():
ret += gen_visit_implicit_struct(var.type)
ret += mcgen('''
......@@ -297,48 +270,57 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
if base:
ret += mcgen('''
visit_type_%(name)s_fields(m, obj, &err);
visit_type_%(c_name)s_fields(m, obj, &err);
if (err) {
goto out_obj;
}
''',
name=c_name(name))
if not discriminator:
tag = 'kind'
disc_key = "type"
else:
tag = discriminator
disc_key = discriminator
c_name=c_name(name))
tag_key = variants.tag_member.name
if not variants.tag_name:
# we pointlessly use a different key for simple unions
tag_key = 'type'
ret += mcgen('''
visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
if (err) {
goto out_obj;
}
if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
goto out_obj;
}
switch ((*obj)->%(c_tag)s) {
switch ((*obj)->%(c_name)s) {
''',
disc_type = disc_type,
c_tag=c_name(tag),
disc_key = disc_key)
for key in members:
if not discriminator:
fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
c_type=variants.tag_member.type.c_name(),
# TODO ugly special case for simple union
# Use same tag name in C as on the wire to get rid of
# it, then: c_name=c_name(variants.tag_member.name)
c_name=c_name(variants.tag_name or 'kind'),
name=tag_key)
for var in variants.variants:
# TODO ugly special case for simple union
simple_union_type = var.simple_union_type()
ret += mcgen('''
case %(case)s:
''',
case=c_enum_const(variants.tag_member.type.name,
var.name))
if simple_union_type:
ret += mcgen('''
visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);
''',
c_type=simple_union_type.c_name(),
c_name=c_name(var.name))
else:
fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
enum_full_value = c_enum_const(disc_type, key)
ret += mcgen('''
visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);
''',
c_type=var.type.c_name(),
c_name=c_name(var.name))
ret += mcgen('''
case %(enum_full_value)s:
''' + fmt + '''
break;
''',
enum_full_value = enum_full_value,
c_type=type_name(members[key]),
c_name=c_name(key))
''')
ret += mcgen('''
default:
......@@ -359,38 +341,59 @@ out:
return ret
def generate_declaration(name, builtin_type=False):
ret = ""
if not builtin_type:
name = c_name(name)
ret += mcgen('''
void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
''',
name=name)
ret += mcgen('''
void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
''',
name=name)
return ret
def generate_enum_declaration(name):
ret = mcgen('''
void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
''',
name=c_name(name))
return ret
def generate_decl_enum(name):
return mcgen('''
void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
''',
name=c_name(name))
class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
def __init__(self):
self.decl = None
self.defn = None
self._btin = None
def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._btin = guardstart('QAPI_VISIT_BUILTIN')
def visit_end(self):
# To avoid header dependency hell, we always generate
# declarations for built-in types in our header files and
# simply guard them. See also do_builtins (command line
# option -b).
self._btin += guardend('QAPI_VISIT_BUILTIN')
self.decl = self._btin + self.decl
self._btin = None
def visit_enum_type(self, name, info, values, prefix):
self.decl += gen_visit_decl(name, scalar=True)
self.defn += gen_visit_enum(name)
def visit_array_type(self, name, info, element_type):
decl = gen_visit_decl(name)
defn = gen_visit_list(name, element_type)
if isinstance(element_type, QAPISchemaBuiltinType):
self._btin += decl
if do_builtins:
self.defn += defn
else:
self.decl += decl
self.defn += defn
def visit_object_type(self, name, info, base, members, variants):
if info:
self.decl += gen_visit_decl(name)
if variants:
assert not members # not implemented
self.defn += gen_visit_union(name, base, variants)
else:
self.defn += gen_visit_struct(name, base, members)
def visit_alternate_type(self, name, info, variants):
self.decl += gen_visit_decl(name)
self.defn += gen_visit_alternate(name, variants)
# If you link code generated from multiple schemata, you want only one
# instance of the code for built-in types. Generate it only when
# do_builtins, enabled by command line option -b. See also
# QAPISchemaGenVisitVisitor.visit_end().
do_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
......@@ -437,7 +440,7 @@ fdef.write(mcgen('''
#include "qemu-common.h"
#include "%(prefix)sqapi-visit.h"
''',
prefix = prefix))
prefix=prefix))
fdecl.write(mcgen('''
#include "qapi/visitor.h"
......@@ -446,56 +449,10 @@ fdecl.write(mcgen('''
''',
prefix=prefix))
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.keys():
fdecl.write(generate_declaration(typename, builtin_type=True))
fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
# ...this doesn't work for cases where we link in multiple objects that
# have the functions defined, so we use -b option to provide control
# over these cases
if do_builtins:
for typename in builtin_types.keys():
fdef.write(generate_visit_list(typename))
for expr in exprs:
if expr.has_key('struct'):
ret = generate_visit_struct(expr)
ret += generate_visit_list(expr['struct'])
fdef.write(ret)
ret = generate_declaration(expr['struct'])
fdecl.write(ret)
elif expr.has_key('union'):
ret = generate_visit_union(expr)
ret += generate_visit_list(expr['union'])
fdef.write(ret)
enum_define = discriminator_find_enum_define(expr)
ret = ""
if not enum_define:
ret = generate_decl_enum('%sKind' % expr['union'])
ret += generate_declaration(expr['union'])
fdecl.write(ret)
elif expr.has_key('alternate'):
ret = generate_visit_alternate(expr['alternate'], expr['data'])
ret += generate_visit_list(expr['alternate'])
fdef.write(ret)
ret = generate_decl_enum('%sKind' % expr['alternate'])
ret += generate_declaration(expr['alternate'])
fdecl.write(ret)
elif expr.has_key('enum'):
ret = generate_visit_list(expr['enum'])
ret += generate_visit_enum(expr['enum'])
fdef.write(ret)
ret = generate_decl_enum(expr['enum'])
ret += generate_enum_declaration(expr['enum'])
fdecl.write(ret)
schema = QAPISchema(input_file)
gen = QAPISchemaGenVisitVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
close_output(fdef, fdecl)
此差异已折叠。
......@@ -35,6 +35,7 @@ test-qmp-commands.h
test-qmp-event
test-qmp-input-strict
test-qmp-input-visitor
test-qmp-introspect.[ch]
test-qmp-marshal.c
test-qmp-output-visitor
test-rcu-list
......
......@@ -234,11 +234,12 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
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 \
type-bypass-bad-gen.json \
args-invalid.json \
args-array-empty.json args-array-unknown.json args-int.json \
args-unknown.json args-member-unknown.json args-member-array.json \
args-member-array-bad.json args-alternate.json args-union.json \
args-any.json \
returns-array-bad.json returns-int.json returns-dict.json \
returns-unknown.json returns-alternate.json returns-whitelist.json \
missing-colon.json missing-comma-list.json missing-comma-object.json \
......@@ -255,7 +256,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
flat-union-invalid-branch-key.json flat-union-reverse-define.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-base-any.json \
flat-union-array-branch.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 \
......@@ -268,7 +269,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
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
tests/test-qmp-commands.h tests/test-qapi-event.h \
tests/test-qmp-introspect.h
test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \
......@@ -288,7 +290,7 @@ QEMU_CFLAGS += -I$(SRC_PATH)/tests
test-util-obj-y = libqemuutil.a libqemustub.a
test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y)
test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
tests/test-qapi-event.o \
tests/test-qapi-event.o tests/test-qmp-introspect.o \
$(test-qom-obj-y)
test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y)
......@@ -345,6 +347,11 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-eve
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
$(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
$(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y)
tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
......
[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
{'enum_name': 'AltKind', 'enum_values': None}]
[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
object :empty
alternate Alt
case value: int
case string: Enum
case struct: Data
enum AltKind ['value', 'string', 'struct']
object Data
member number: int optional=True
member name: str optional=True
enum Enum ['hello', 'world']
tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any'
# we do not allow an 'any' argument
{ 'command': 'oops', 'data': 'any' }
[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]),
OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))]),
OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])]
[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}]
[OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))])]
object :empty
object :obj-okay-arg
member member1: intList optional=False
member member2: defList optional=False
enum abc ['a', 'b', 'c']
object def
member array: abcList optional=False
command okay :obj-okay-arg -> None
gen=True success_response=True
[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
[]
object :empty
enum Status ['good', 'bad', 'ugly']
[OrderedDict([('enum', 'MyEnum'), ('data', [])])]
[{'enum_name': 'MyEnum', 'enum_values': []}]
[]
object :empty
enum MyEnum []
[OrderedDict([('event', 'oops')])]
[]
[]
object :empty
event oops None
tests/qapi-schema/flat-union-base-any.json:8: Base 'any' is not a valid struct
......@@ -6,7 +6,7 @@
{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
'base': '**',
'base': 'any',
'discriminator': 'enum1',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid struct
[OrderedDict([('command', 'fooA'), ('data', OrderedDict([('bar1', 'str')]))])]
[]
[]
object :empty
object :obj-fooA-arg
member bar1: str optional=False
command fooA :obj-fooA-arg -> None
gen=True success_response=True
[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
[]
object :empty
enum Status ['good', 'bad', 'ugly']
此差异已折叠。
此差异已折叠。
此差异已折叠。
tests/qapi-schema/type-bypass-no-gen.json:2: Member 'arg' of 'data' for command 'unsafe' uses '**' but did not request 'gen':false
# type bypass only works with 'gen':false
{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**' }
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册