2018-02-11 12:35:41 +03:00
|
|
|
"""
|
|
|
|
QAPI command marshaller generator
|
|
|
|
|
|
|
|
Copyright IBM, Corp. 2011
|
|
|
|
Copyright (C) 2014-2018 Red Hat, Inc.
|
|
|
|
|
|
|
|
Authors:
|
|
|
|
Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
Michael Roth <mdroth@linux.vnet.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.
|
|
|
|
"""
|
2011-07-19 23:50:42 +04:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
from typing import (
|
|
|
|
Dict,
|
|
|
|
List,
|
|
|
|
Optional,
|
|
|
|
Set,
|
|
|
|
)
|
|
|
|
|
2020-10-09 19:15:39 +03:00
|
|
|
from .common import c_name, mcgen
|
|
|
|
from .gen import (
|
2020-10-09 19:15:44 +03:00
|
|
|
QAPIGenC,
|
2020-10-09 19:15:39 +03:00
|
|
|
QAPISchemaModularCVisitor,
|
|
|
|
build_params,
|
2021-10-28 13:25:17 +03:00
|
|
|
gen_special_features,
|
2022-02-11 21:36:50 +03:00
|
|
|
ifcontext,
|
2020-10-09 19:15:39 +03:00
|
|
|
)
|
2020-10-09 19:15:44 +03:00
|
|
|
from .schema import (
|
|
|
|
QAPISchema,
|
|
|
|
QAPISchemaFeature,
|
2021-08-04 11:30:57 +03:00
|
|
|
QAPISchemaIfCond,
|
2020-10-09 19:15:44 +03:00
|
|
|
QAPISchemaObjectType,
|
|
|
|
QAPISchemaType,
|
|
|
|
)
|
|
|
|
from .source import QAPISourceInfo
|
2011-07-19 23:50:42 +04:00
|
|
|
|
2015-09-16 14:06:16 +03:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def gen_command_decl(name: str,
|
|
|
|
arg_type: Optional[QAPISchemaObjectType],
|
|
|
|
boxed: bool,
|
2022-10-13 11:50:49 +03:00
|
|
|
ret_type: Optional[QAPISchemaType],
|
|
|
|
coroutine: bool) -> str:
|
2011-07-19 23:50:42 +04:00
|
|
|
return mcgen('''
|
2022-10-13 11:50:49 +03:00
|
|
|
%(c_type)s %(coroutine_fn)sqmp_%(c_name)s(%(params)s);
|
2011-07-19 23:50:42 +04:00
|
|
|
''',
|
2015-09-16 14:06:16 +03:00
|
|
|
c_type=(ret_type and ret_type.c_type()) or 'void',
|
2022-10-13 11:50:49 +03:00
|
|
|
coroutine_fn='coroutine_fn ' if coroutine else '',
|
2015-09-16 14:06:16 +03:00
|
|
|
c_name=c_name(name),
|
2017-06-01 15:41:41 +03:00
|
|
|
params=build_params(arg_type, boxed, 'Error **errp'))
|
2015-09-16 14:06:16 +03:00
|
|
|
|
2011-07-19 23:50:42 +04:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def gen_call(name: str,
|
|
|
|
arg_type: Optional[QAPISchemaObjectType],
|
|
|
|
boxed: bool,
|
2022-01-26 19:11:26 +03:00
|
|
|
ret_type: Optional[QAPISchemaType],
|
|
|
|
gen_tracing: bool) -> str:
|
2015-09-16 14:06:16 +03:00
|
|
|
ret = ''
|
|
|
|
|
|
|
|
argstr = ''
|
2016-07-14 06:50:19 +03:00
|
|
|
if boxed:
|
2019-09-13 23:13:41 +03:00
|
|
|
assert arg_type
|
2016-07-14 06:50:20 +03:00
|
|
|
argstr = '&arg, '
|
2016-07-14 06:50:19 +03:00
|
|
|
elif arg_type:
|
2024-03-15 18:33:23 +03:00
|
|
|
assert not arg_type.branches
|
2015-09-16 14:06:16 +03:00
|
|
|
for memb in arg_type.members:
|
2023-03-16 10:13:25 +03:00
|
|
|
assert not memb.ifcond.is_present()
|
qapi: Start to elide redundant has_FOO in generated C
In QAPI, absent optional members are distinct from any present value.
We thus represent an optional schema member FOO as two C members: a
FOO with the member's type, and a bool has_FOO. Likewise for function
arguments.
However, has_FOO is actually redundant for a pointer-valued FOO, which
can be null only when has_FOO is false, i.e. has_FOO == !!FOO. Except
for arrays, where we a null FOO can also be a present empty array.
The redundant has_FOO are a nuisance to work with. Improve the
generator to elide them. Uses of has_FOO need to be replaced as
follows.
Tests of has_FOO become the equivalent comparison of FOO with null.
For brevity, this is commonly done by implicit conversion to bool.
Assignments to has_FOO get dropped.
Likewise for arguments to has_FOO parameters.
Beware: code may violate the invariant has_FOO == !!FOO before the
transformation, and get away with it. The above transformation can
then break things. Two cases:
* Absent: if code ignores FOO entirely when !has_FOO (except for
freeing it if necessary), even non-null / uninitialized FOO works.
Such code is known to exist.
* Present: if code ignores FOO entirely when has_FOO, even null FOO
works. Such code should not exist.
In both cases, replacing tests of has_FOO by FOO reverts their sense.
We have to fix the value of FOO then.
To facilitate review of the necessary updates to handwritten code, add
means to opt out of this change, and opt out for all QAPI schema
modules where the change requires updates to handwritten code. The
next few commits will remove these opt-outs in reviewable chunks, then
drop the means to opt out.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20221104160712.3005652-5-armbru@redhat.com>
2022-11-04 19:06:46 +03:00
|
|
|
if memb.need_has():
|
qapi-commands: Utilize implicit struct visits
Rather than generate inline per-member visits, take advantage
of the 'visit_type_FOO_members()' function for command
marshalling. This is possible now that implicit structs can be
visited like any other. Generate call arguments from a stack-
allocated struct, rather than a list of local variables:
|@@ -57,26 +57,15 @@ void qmp_marshal_add_fd(QDict *args, QOb
| QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
| QapiDeallocVisitor *qdv;
| Visitor *v;
|- bool has_fdset_id = false;
|- int64_t fdset_id = 0;
|- bool has_opaque = false;
|- char *opaque = NULL;
|+ q_obj_add_fd_arg arg = {0};
|
| v = qmp_input_get_visitor(qiv);
|- if (visit_optional(v, "fdset-id", &has_fdset_id)) {
|- visit_type_int(v, "fdset-id", &fdset_id, &err);
|- if (err) {
|- goto out;
|- }
|- }
|- if (visit_optional(v, "opaque", &has_opaque)) {
|- visit_type_str(v, "opaque", &opaque, &err);
|- if (err) {
|- goto out;
|- }
|+ visit_type_q_obj_add_fd_arg_members(v, &arg, &err);
|+ if (err) {
|+ goto out;
| }
|
|- retval = qmp_add_fd(has_fdset_id, fdset_id, has_opaque, opaque, &err);
|+ retval = qmp_add_fd(arg.has_fdset_id, arg.fdset_id, arg.has_opaque, arg.opaque, &err);
| if (err) {
| goto out;
| }
|@@ -88,12 +77,7 @@ out:
| qmp_input_visitor_cleanup(qiv);
| qdv = qapi_dealloc_visitor_new();
| v = qapi_dealloc_get_visitor(qdv);
|- if (visit_optional(v, "fdset-id", &has_fdset_id)) {
|- visit_type_int(v, "fdset-id", &fdset_id, NULL);
|- }
|- if (visit_optional(v, "opaque", &has_opaque)) {
|- visit_type_str(v, "opaque", &opaque, NULL);
|- }
|+ visit_type_q_obj_add_fd_arg_members(v, &arg, NULL);
| qapi_dealloc_visitor_cleanup(qdv);
| }
This also has the nice side effect of eliminating a chance of
collision between argument QMP names and local variables.
This patch also paves the way for some followup simplifications
in the generator, in subsequent patches.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1458254921-17042-9-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-18 01:48:33 +03:00
|
|
|
argstr += 'arg.has_%s, ' % c_name(memb.name)
|
|
|
|
argstr += 'arg.%s, ' % c_name(memb.name)
|
2015-09-16 14:06:16 +03:00
|
|
|
|
|
|
|
lhs = ''
|
|
|
|
if ret_type:
|
|
|
|
lhs = 'retval = '
|
|
|
|
|
2022-01-26 19:11:26 +03:00
|
|
|
name = c_name(name)
|
|
|
|
upper = name.upper()
|
|
|
|
|
|
|
|
if gen_tracing:
|
|
|
|
ret += mcgen('''
|
|
|
|
|
|
|
|
if (trace_event_get_state_backends(TRACE_QMP_ENTER_%(upper)s)) {
|
|
|
|
g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args));
|
|
|
|
|
|
|
|
trace_qmp_enter_%(name)s(req_json->str);
|
|
|
|
}
|
2022-11-04 19:06:44 +03:00
|
|
|
''',
|
2022-01-26 19:11:26 +03:00
|
|
|
upper=upper, name=name)
|
|
|
|
|
|
|
|
ret += mcgen('''
|
2015-09-16 14:06:18 +03:00
|
|
|
|
2022-01-26 19:11:26 +03:00
|
|
|
%(lhs)sqmp_%(name)s(%(args)s&err);
|
2011-07-19 23:50:42 +04:00
|
|
|
''',
|
2022-01-26 19:11:26 +03:00
|
|
|
name=name, args=argstr, lhs=lhs)
|
2022-01-26 19:11:25 +03:00
|
|
|
|
|
|
|
ret += mcgen('''
|
2016-07-14 06:50:17 +03:00
|
|
|
if (err) {
|
2022-01-26 19:11:26 +03:00
|
|
|
''')
|
|
|
|
|
|
|
|
if gen_tracing:
|
|
|
|
ret += mcgen('''
|
|
|
|
trace_qmp_exit_%(name)s(error_get_pretty(err), false);
|
|
|
|
''',
|
|
|
|
name=name)
|
|
|
|
|
|
|
|
ret += mcgen('''
|
2022-01-26 19:11:25 +03:00
|
|
|
error_propagate(errp, err);
|
2016-07-14 06:50:17 +03:00
|
|
|
goto out;
|
|
|
|
}
|
2022-01-26 19:11:25 +03:00
|
|
|
''')
|
|
|
|
|
|
|
|
if ret_type:
|
|
|
|
ret += mcgen('''
|
2015-06-27 18:21:12 +03:00
|
|
|
|
2020-07-07 19:06:08 +03:00
|
|
|
qmp_marshal_output_%(c_name)s(retval, ret, errp);
|
2011-07-19 23:50:42 +04:00
|
|
|
''',
|
2015-09-16 14:06:21 +03:00
|
|
|
c_name=ret_type.c_name())
|
2022-01-26 19:11:26 +03:00
|
|
|
|
|
|
|
if gen_tracing:
|
|
|
|
if ret_type:
|
|
|
|
ret += mcgen('''
|
|
|
|
|
|
|
|
if (trace_event_get_state_backends(TRACE_QMP_EXIT_%(upper)s)) {
|
|
|
|
g_autoptr(GString) ret_json = qobject_to_json(*ret);
|
|
|
|
|
|
|
|
trace_qmp_exit_%(name)s(ret_json->str, true);
|
|
|
|
}
|
2022-11-04 19:06:44 +03:00
|
|
|
''',
|
2022-01-26 19:11:26 +03:00
|
|
|
upper=upper, name=name)
|
|
|
|
else:
|
|
|
|
ret += mcgen('''
|
|
|
|
|
|
|
|
trace_qmp_exit_%(name)s("{}", true);
|
2022-11-04 19:06:44 +03:00
|
|
|
''',
|
2022-01-26 19:11:26 +03:00
|
|
|
name=name)
|
|
|
|
|
2015-06-27 18:49:34 +03:00
|
|
|
return ret
|
2011-07-19 23:50:42 +04:00
|
|
|
|
2015-09-16 14:06:16 +03:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def gen_marshal_output(ret_type: QAPISchemaType) -> str:
|
2015-09-16 14:06:18 +03:00
|
|
|
return mcgen('''
|
2015-09-16 14:06:11 +03:00
|
|
|
|
2020-10-09 19:15:30 +03:00
|
|
|
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in,
|
|
|
|
QObject **ret_out, Error **errp)
|
2011-07-19 23:50:42 +04:00
|
|
|
{
|
|
|
|
Visitor *v;
|
|
|
|
|
qapi: Implement deprecated-output=hide for QMP command results
This policy suppresses deprecated bits in output, and thus permits
"testing the future". Implement it for QMP command results. Example:
when QEMU is run with -compat deprecated-output=hide, then
{"execute": "query-cpus-fast"}
yields
{"return": [{"thread-id": 9805, "props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "qom-path": "/machine/unattached/device[0]", "cpu-index": 0, "target": "x86_64"}]}
instead of
{"return": [{"arch": "x86", "thread-id": 22436, "props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "qom-path": "/machine/unattached/device[0]", "cpu-index": 0, "target": "x86_64"}]}
Note the suppression of deprecated member "arch".
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210318155519.1224118-4-armbru@redhat.com>
2021-03-18 18:55:11 +03:00
|
|
|
v = qobject_output_visitor_new_qmp(ret_out);
|
2020-07-07 19:06:08 +03:00
|
|
|
if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
|
qapi: Add new visit_complete() function
Making each output visitor provide its own output collection
function was the only remaining reason for exposing visitor
sub-types to the rest of the code base. Add a polymorphic
visit_complete() function which is a no-op for input visitors,
and which populates an opaque pointer for output visitors. For
maximum type-safety, also add a parameter to the output visitor
constructors with a type-correct version of the output pointer,
and assert that the two uses match.
This approach was considered superior to either passing the
output parameter only during construction (action at a distance
during visit_free() feels awkward) or only during visit_complete()
(defeating type safety makes it easier to use incorrectly).
Most callers were function-local, and therefore a mechanical
conversion; the testsuite was a bit trickier, but the previous
cleanup patch minimized the churn here.
The visit_complete() function may be called at most once; doing
so lets us use transfer semantics rather than duplication or
ref-count semantics to get the just-built output back to the
caller, even though it means our behavior is not idempotent.
Generated code is simplified as follows for events:
|@@ -26,7 +26,7 @@ void qapi_event_send_acpi_device_ost(ACP
| QDict *qmp;
| Error *err = NULL;
| QMPEventFuncEmit emit;
|- QmpOutputVisitor *qov;
|+ QObject *obj;
| Visitor *v;
| q_obj_ACPI_DEVICE_OST_arg param = {
| info
|@@ -39,8 +39,7 @@ void qapi_event_send_acpi_device_ost(ACP
|
| qmp = qmp_event_build_dict("ACPI_DEVICE_OST");
|
|- qov = qmp_output_visitor_new();
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(&obj);
|
| visit_start_struct(v, "ACPI_DEVICE_OST", NULL, 0, &err);
| if (err) {
|@@ -55,7 +54,8 @@ void qapi_event_send_acpi_device_ost(ACP
| goto out;
| }
|
|- qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov));
|+ visit_complete(v, &obj);
|+ qdict_put_obj(qmp, "data", obj);
| emit(QAPI_EVENT_ACPI_DEVICE_OST, qmp, &err);
and for commands:
| {
| Error *err = NULL;
|- QmpOutputVisitor *qov = qmp_output_visitor_new();
| Visitor *v;
|
|- v = qmp_output_get_visitor(qov);
|+ v = qmp_output_visitor_new(ret_out);
| visit_type_AddfdInfo(v, "unused", &ret_in, &err);
|- if (err) {
|- goto out;
|+ if (!err) {
|+ visit_complete(v, ret_out);
| }
|- *ret_out = qmp_output_get_qobject(qov);
|-
|-out:
| error_propagate(errp, err);
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-13-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:43 +03:00
|
|
|
visit_complete(v, ret_out);
|
2011-07-19 23:50:42 +04:00
|
|
|
}
|
qapi: Add new visit_free() function
Making each visitor provide its own (awkwardly-named) FOO_cleanup()
is unusual, when we can instead have a polymorphic visit_free()
interface. Over the next few patches, we can use the polymorphic
functions to eliminate the need for a FOO_get_visitor() function
for accessing specific visitor functionality, once everything can
be accessed directly through the Visitor* interfaces.
The dealloc visitor is the first one converted to completely use
the new entry point, since qapi_dealloc_visitor_cleanup() was the
only reason that qapi_dealloc_get_visitor() existed, and only
generated and testsuite code was even using it. With the new
visit_free() entry point in place, we no longer need to expose
the QapiDeallocVisitor subtype through qapi_dealloc_visitor_new(),
and can get by with less generated code, with diffs that look like:
| void qapi_free_ACPIOSTInfo(ACPIOSTInfo *obj)
| {
|- QapiDeallocVisitor *qdv;
| Visitor *v;
|
| if (!obj) {
| return;
| }
|
|- qdv = qapi_dealloc_visitor_new();
|- v = qapi_dealloc_get_visitor(qdv);
|+ v = qapi_dealloc_visitor_new();
| visit_type_ACPIOSTInfo(v, NULL, &obj, NULL);
|- qapi_dealloc_visitor_cleanup(qdv);
|+ visit_free(v);
|}
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-5-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:35 +03:00
|
|
|
visit_free(v);
|
|
|
|
v = qapi_dealloc_visitor_new();
|
qapi: Swap visit_* arguments for consistent 'name' placement
JSON uses "name":value, but many of our visitor interfaces were
called with visit_type_FOO(v, &value, name, errp). This can be
a bit confusing to have to mentally swap the parameter order to
match JSON order. It's particularly bad for visit_start_struct(),
where the 'name' parameter is smack in the middle of the
otherwise-related group of 'obj, kind, size' parameters! It's
time to do a global swap of the parameter ordering, so that the
'name' parameter is always immediately after the Visitor argument.
Additional reason in favor of the swap: the existing include/qjson.h
prefers listing 'name' first in json_prop_*(), and I have plans to
unify that file with the qapi visitors; listing 'name' first in
qapi will minimize churn to the (admittedly few) qjson.h clients.
Later patches will then fix docs, object.h, visitor-impl.h, and
those clients to match.
Done by first patching scripts/qapi*.py by hand to make generated
files do what I want, then by running the following Coccinelle
script to affect the rest of the code base:
$ spatch --sp-file script `git grep -l '\bvisit_' -- '**/*.[ch]'`
I then had to apply some touchups (Coccinelle insisted on TAB
indentation in visitor.h, and botched the signature of
visit_type_enum() by rewriting 'const char *const strings[]' to
the syntactically invalid 'const char*const[] strings'). The
movement of parameters is sufficient to provoke compiler errors
if any callers were missed.
// Part 1: Swap declaration order
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_start_struct
-(TV v, TObj OBJ, T1 ARG1, const char *name, T2 ARG2, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type bool, TV, T1;
identifier ARG1;
@@
bool visit_optional
-(TV v, T1 ARG1, const char *name)
+(TV v, const char *name, T1 ARG1)
{ ... }
@@
type TV, TErr, TObj, T1;
identifier OBJ, ARG1;
@@
void visit_get_next_type
-(TV v, TObj OBJ, T1 ARG1, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, TErr errp)
{ ... }
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_type_enum
-(TV v, TObj OBJ, T1 ARG1, T2 ARG2, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type TV, TErr, TObj;
identifier OBJ;
identifier VISIT_TYPE =~ "^visit_type_";
@@
void VISIT_TYPE
-(TV v, TObj OBJ, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, TErr errp)
{ ... }
// Part 2: swap caller order
@@
expression V, NAME, OBJ, ARG1, ARG2, ERR;
identifier VISIT_TYPE =~ "^visit_type_";
@@
(
-visit_start_struct(V, OBJ, ARG1, NAME, ARG2, ERR)
+visit_start_struct(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-visit_optional(V, ARG1, NAME)
+visit_optional(V, NAME, ARG1)
|
-visit_get_next_type(V, OBJ, ARG1, NAME, ERR)
+visit_get_next_type(V, NAME, OBJ, ARG1, ERR)
|
-visit_type_enum(V, OBJ, ARG1, ARG2, NAME, ERR)
+visit_type_enum(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-VISIT_TYPE(V, OBJ, NAME, ERR)
+VISIT_TYPE(V, NAME, OBJ, ERR)
)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <1454075341-13658-19-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-01-29 16:48:54 +03:00
|
|
|
visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
|
qapi: Add new visit_free() function
Making each visitor provide its own (awkwardly-named) FOO_cleanup()
is unusual, when we can instead have a polymorphic visit_free()
interface. Over the next few patches, we can use the polymorphic
functions to eliminate the need for a FOO_get_visitor() function
for accessing specific visitor functionality, once everything can
be accessed directly through the Visitor* interfaces.
The dealloc visitor is the first one converted to completely use
the new entry point, since qapi_dealloc_visitor_cleanup() was the
only reason that qapi_dealloc_get_visitor() existed, and only
generated and testsuite code was even using it. With the new
visit_free() entry point in place, we no longer need to expose
the QapiDeallocVisitor subtype through qapi_dealloc_visitor_new(),
and can get by with less generated code, with diffs that look like:
| void qapi_free_ACPIOSTInfo(ACPIOSTInfo *obj)
| {
|- QapiDeallocVisitor *qdv;
| Visitor *v;
|
| if (!obj) {
| return;
| }
|
|- qdv = qapi_dealloc_visitor_new();
|- v = qapi_dealloc_get_visitor(qdv);
|+ v = qapi_dealloc_visitor_new();
| visit_type_ACPIOSTInfo(v, NULL, &obj, NULL);
|- qapi_dealloc_visitor_cleanup(qdv);
|+ visit_free(v);
|}
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-5-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:35 +03:00
|
|
|
visit_free(v);
|
2011-07-19 23:50:42 +04:00
|
|
|
}
|
|
|
|
''',
|
2015-09-16 14:06:21 +03:00
|
|
|
c_type=ret_type.c_type(), c_name=ret_type.c_name())
|
2011-07-19 23:50:42 +04:00
|
|
|
|
2015-09-16 14:06:16 +03:00
|
|
|
|
2022-10-13 11:50:49 +03:00
|
|
|
def build_marshal_proto(name: str,
|
|
|
|
coroutine: bool) -> str:
|
|
|
|
return ('void %(coroutine_fn)sqmp_marshal_%(c_name)s(%(params)s)' % {
|
|
|
|
'coroutine_fn': 'coroutine_fn ' if coroutine else '',
|
|
|
|
'c_name': c_name(name),
|
|
|
|
'params': 'QDict *args, QObject **ret, Error **errp',
|
|
|
|
})
|
2011-09-02 21:34:46 +04:00
|
|
|
|
2015-09-16 14:06:16 +03:00
|
|
|
|
2022-10-13 11:50:49 +03:00
|
|
|
def gen_marshal_decl(name: str,
|
|
|
|
coroutine: bool) -> str:
|
2015-09-16 14:06:18 +03:00
|
|
|
return mcgen('''
|
|
|
|
%(proto)s;
|
|
|
|
''',
|
2022-10-13 11:50:49 +03:00
|
|
|
proto=build_marshal_proto(name, coroutine))
|
2015-09-16 14:06:18 +03:00
|
|
|
|
2011-09-02 21:34:46 +04:00
|
|
|
|
2022-01-26 19:11:26 +03:00
|
|
|
def gen_trace(name: str) -> str:
|
|
|
|
return mcgen('''
|
|
|
|
qmp_enter_%(name)s(const char *json) "%%s"
|
|
|
|
qmp_exit_%(name)s(const char *result, bool succeeded) "%%s %%d"
|
|
|
|
''',
|
|
|
|
name=c_name(name))
|
|
|
|
|
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def gen_marshal(name: str,
|
|
|
|
arg_type: Optional[QAPISchemaObjectType],
|
|
|
|
boxed: bool,
|
2022-01-26 19:11:26 +03:00
|
|
|
ret_type: Optional[QAPISchemaType],
|
2022-10-13 11:50:49 +03:00
|
|
|
gen_tracing: bool,
|
|
|
|
coroutine: bool) -> str:
|
2019-09-13 23:13:41 +03:00
|
|
|
have_args = boxed or (arg_type and not arg_type.is_empty())
|
2021-02-01 22:37:32 +03:00
|
|
|
if have_args:
|
|
|
|
assert arg_type is not None
|
|
|
|
arg_type_c_name = arg_type.c_name()
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
|
2011-07-19 23:50:42 +04:00
|
|
|
ret = mcgen('''
|
2015-09-16 14:06:11 +03:00
|
|
|
|
2015-09-16 14:06:18 +03:00
|
|
|
%(proto)s
|
2011-07-19 23:50:42 +04:00
|
|
|
{
|
2016-03-18 01:48:34 +03:00
|
|
|
Error *err = NULL;
|
2020-07-07 19:06:08 +03:00
|
|
|
bool ok = false;
|
2020-04-24 11:43:37 +03:00
|
|
|
Visitor *v;
|
2011-07-19 23:50:42 +04:00
|
|
|
''',
|
2022-10-13 11:50:49 +03:00
|
|
|
proto=build_marshal_proto(name, coroutine))
|
2011-07-19 23:50:42 +04:00
|
|
|
|
2016-03-18 01:48:34 +03:00
|
|
|
if ret_type:
|
|
|
|
ret += mcgen('''
|
|
|
|
%(c_type)s retval;
|
|
|
|
''',
|
|
|
|
c_type=ret_type.c_type())
|
|
|
|
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
if have_args:
|
2016-03-18 01:48:34 +03:00
|
|
|
ret += mcgen('''
|
|
|
|
%(c_name)s arg = {0};
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
''',
|
2021-02-01 22:37:32 +03:00
|
|
|
c_name=arg_type_c_name)
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
|
|
|
|
ret += mcgen('''
|
2020-04-24 11:43:37 +03:00
|
|
|
|
2021-03-18 18:55:18 +03:00
|
|
|
v = qobject_input_visitor_new_qmp(QOBJECT(args));
|
2020-07-07 19:06:08 +03:00
|
|
|
if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
|
qapi-commands: Wrap argument visit in visit_start_struct
The qmp-input visitor was allowing callers to play rather fast
and loose: when visiting a QDict, you could grab members of the
root dictionary without first pushing into the dict; among the
culprit callers was the generated marshal code on the 'arguments'
dictionary of a QMP command. But we are about to tighten the
input visitor, at which point the generated marshal code MUST
follow the same paradigms as everyone else, of pushing into the
struct before grabbing its keys.
Generated code grows as follows:
|@@ -515,7 +641,12 @@ void qmp_marshal_blockdev_backup(QDict *
| BlockdevBackup arg = {0};
|
| v = qmp_input_get_visitor(qiv);
|+ visit_start_struct(v, NULL, NULL, 0, &err);
|+ if (err) {
|+ goto out;
|+ }
| visit_type_BlockdevBackup_members(v, &arg, &err);
|+ visit_end_struct(v, err ? NULL : &err);
| if (err) {
| goto out;
| }
|@@ -527,7 +715,9 @@ out:
| qmp_input_visitor_cleanup(qiv);
| qdv = qapi_dealloc_visitor_new();
| v = qapi_dealloc_get_visitor(qdv);
|+ visit_start_struct(v, NULL, NULL, 0, NULL);
| visit_type_BlockdevBackup_members(v, &arg, NULL);
|+ visit_end_struct(v, NULL);
| qapi_dealloc_visitor_cleanup(qdv);
| }
The use of 'err ? NULL : &err' is temporary; a later patch will
clean that up when it splits visit_end_struct().
Prior to this patch, the fact that there was no final
visit_end_struct() meant that even though we are using a strict
input visit, the marshalling code was not detecting excess input
at the top level (only in nested levels). Fortunately, we have
code in monitor.c:qmp_check_client_args() that also checks for
no excess arguments at the top level. But as the generated code
is more compact than the manual check, a later patch will clean
up monitor.c to drop the redundancy added here.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1461879932-9020-9-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:16 +03:00
|
|
|
goto out;
|
|
|
|
}
|
2020-04-24 11:43:38 +03:00
|
|
|
''')
|
|
|
|
|
|
|
|
if have_args:
|
|
|
|
ret += mcgen('''
|
2020-07-07 19:06:08 +03:00
|
|
|
if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) {
|
|
|
|
ok = visit_check_struct(v, errp);
|
qapi: Split visit_end_struct() into pieces
As mentioned in previous patches, we want to call visit_end_struct()
functions unconditionally, so that visitors can release resources
tied up since the matching visit_start_struct() without also having
to worry about error priority if more than one error occurs.
Even though error_propagate() can be safely used to ignore a second
error during cleanup caused by a first error, it is simpler if the
cleanup cannot set an error. So, split out the error checking
portion (basically, input visitors checking for unvisited keys) into
a new function visit_check_struct(), which can be safely skipped if
any earlier errors are encountered, and leave the cleanup portion
(which never fails, but must be called unconditionally if
visit_start_struct() succeeded) in visit_end_struct().
Generated code in qapi-visit.c has diffs resembling:
|@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v,
| goto out_obj;
| }
| visit_type_ACPIOSTInfo_members(v, obj, &err);
|- error_propagate(errp, err);
|- err = NULL;
|+ if (err) {
|+ goto out_obj;
|+ }
|+ visit_check_struct(v, &err);
| out_obj:
|- visit_end_struct(v, &err);
|+ visit_end_struct(v);
| out:
and in qapi-event.c:
@@ -47,7 +47,10 @@ void qapi_event_send_acpi_device_ost(ACP
| goto out;
| }
| visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, ¶m, &err);
|- visit_end_struct(v, err ? NULL : &err);
|+ if (!err) {
|+ visit_check_struct(v, &err);
|+ }
|+ visit_end_struct(v);
| if (err) {
| goto out;
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1461879932-9020-20-git-send-email-eblake@redhat.com>
[Conflict with a doc fixup resolved]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:27 +03:00
|
|
|
}
|
2020-04-24 11:43:38 +03:00
|
|
|
''',
|
2021-02-01 22:37:32 +03:00
|
|
|
c_arg_type=arg_type_c_name)
|
2020-04-24 11:43:38 +03:00
|
|
|
else:
|
|
|
|
ret += mcgen('''
|
2020-07-07 19:06:08 +03:00
|
|
|
ok = visit_check_struct(v, errp);
|
2020-04-24 11:43:38 +03:00
|
|
|
''')
|
|
|
|
|
|
|
|
ret += mcgen('''
|
qapi: Add parameter to visit_end_*
Rather than making the dealloc visitor track of stack of pointers
remembered during visit_start_* in order to free them during
visit_end_*, it's a lot easier to just make all callers pass the
same pointer to visit_end_*. The generated code has access to the
same pointer, while all other users are doing virtual walks and
can pass NULL. The dealloc visitor is then greatly simplified.
All three visit_end_*() functions intentionally take a void**,
even though the visit_start_*() functions differ between void**,
GenericList**, and GenericAlternate**. This is done for several
reasons: when doing a virtual walk, passing NULL doesn't care
what the type is, but when doing a generated walk, we already
have to cast the caller's specific FOO* to call visit_start,
while using void** lets us use visit_end without a cast. Also,
an upcoming patch will add a clone visitor that wants to use
the same implementation for all three visit_end callbacks,
which is made easier if all three share the same signature.
For visitors with already track per-object state (the QMP visitors
via a stack, and the string visitors which do not allow nesting),
add an assertion that the caller is indeed passing the same
pointer to paired calls.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-4-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:34 +03:00
|
|
|
visit_end_struct(v, NULL);
|
2020-07-07 19:06:08 +03:00
|
|
|
if (!ok) {
|
2016-03-18 01:48:34 +03:00
|
|
|
goto out;
|
|
|
|
}
|
2020-04-24 11:43:38 +03:00
|
|
|
''')
|
2016-03-18 01:48:34 +03:00
|
|
|
|
2022-01-26 19:11:26 +03:00
|
|
|
ret += gen_call(name, arg_type, boxed, ret_type, gen_tracing)
|
2015-06-27 18:49:34 +03:00
|
|
|
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
ret += mcgen('''
|
2011-07-19 23:50:42 +04:00
|
|
|
|
|
|
|
out:
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
visit_free(v);
|
2015-06-27 18:49:34 +03:00
|
|
|
''')
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
|
|
|
|
ret += mcgen('''
|
qapi: Add new visit_free() function
Making each visitor provide its own (awkwardly-named) FOO_cleanup()
is unusual, when we can instead have a polymorphic visit_free()
interface. Over the next few patches, we can use the polymorphic
functions to eliminate the need for a FOO_get_visitor() function
for accessing specific visitor functionality, once everything can
be accessed directly through the Visitor* interfaces.
The dealloc visitor is the first one converted to completely use
the new entry point, since qapi_dealloc_visitor_cleanup() was the
only reason that qapi_dealloc_get_visitor() existed, and only
generated and testsuite code was even using it. With the new
visit_free() entry point in place, we no longer need to expose
the QapiDeallocVisitor subtype through qapi_dealloc_visitor_new(),
and can get by with less generated code, with diffs that look like:
| void qapi_free_ACPIOSTInfo(ACPIOSTInfo *obj)
| {
|- QapiDeallocVisitor *qdv;
| Visitor *v;
|
| if (!obj) {
| return;
| }
|
|- qdv = qapi_dealloc_visitor_new();
|- v = qapi_dealloc_get_visitor(qdv);
|+ v = qapi_dealloc_visitor_new();
| visit_type_ACPIOSTInfo(v, NULL, &obj, NULL);
|- qapi_dealloc_visitor_cleanup(qdv);
|+ visit_free(v);
|}
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-5-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:35 +03:00
|
|
|
v = qapi_dealloc_visitor_new();
|
qapi-commands: Wrap argument visit in visit_start_struct
The qmp-input visitor was allowing callers to play rather fast
and loose: when visiting a QDict, you could grab members of the
root dictionary without first pushing into the dict; among the
culprit callers was the generated marshal code on the 'arguments'
dictionary of a QMP command. But we are about to tighten the
input visitor, at which point the generated marshal code MUST
follow the same paradigms as everyone else, of pushing into the
struct before grabbing its keys.
Generated code grows as follows:
|@@ -515,7 +641,12 @@ void qmp_marshal_blockdev_backup(QDict *
| BlockdevBackup arg = {0};
|
| v = qmp_input_get_visitor(qiv);
|+ visit_start_struct(v, NULL, NULL, 0, &err);
|+ if (err) {
|+ goto out;
|+ }
| visit_type_BlockdevBackup_members(v, &arg, &err);
|+ visit_end_struct(v, err ? NULL : &err);
| if (err) {
| goto out;
| }
|@@ -527,7 +715,9 @@ out:
| qmp_input_visitor_cleanup(qiv);
| qdv = qapi_dealloc_visitor_new();
| v = qapi_dealloc_get_visitor(qdv);
|+ visit_start_struct(v, NULL, NULL, 0, NULL);
| visit_type_BlockdevBackup_members(v, &arg, NULL);
|+ visit_end_struct(v, NULL);
| qapi_dealloc_visitor_cleanup(qdv);
| }
The use of 'err ? NULL : &err' is temporary; a later patch will
clean that up when it splits visit_end_struct().
Prior to this patch, the fact that there was no final
visit_end_struct() meant that even though we are using a strict
input visit, the marshalling code was not detecting excess input
at the top level (only in nested levels). Fortunately, we have
code in monitor.c:qmp_check_client_args() that also checks for
no excess arguments at the top level. But as the generated code
is more compact than the manual check, a later patch will clean
up monitor.c to drop the redundancy added here.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1461879932-9020-9-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-29 00:45:16 +03:00
|
|
|
visit_start_struct(v, NULL, NULL, 0, NULL);
|
2020-04-24 11:43:38 +03:00
|
|
|
''')
|
|
|
|
|
|
|
|
if have_args:
|
|
|
|
ret += mcgen('''
|
|
|
|
visit_type_%(c_arg_type)s_members(v, &arg, NULL);
|
|
|
|
''',
|
2021-02-01 22:37:32 +03:00
|
|
|
c_arg_type=arg_type_c_name)
|
2020-04-24 11:43:38 +03:00
|
|
|
|
|
|
|
ret += mcgen('''
|
qapi: Add parameter to visit_end_*
Rather than making the dealloc visitor track of stack of pointers
remembered during visit_start_* in order to free them during
visit_end_*, it's a lot easier to just make all callers pass the
same pointer to visit_end_*. The generated code has access to the
same pointer, while all other users are doing virtual walks and
can pass NULL. The dealloc visitor is then greatly simplified.
All three visit_end_*() functions intentionally take a void**,
even though the visit_start_*() functions differ between void**,
GenericList**, and GenericAlternate**. This is done for several
reasons: when doing a virtual walk, passing NULL doesn't care
what the type is, but when doing a generated walk, we already
have to cast the caller's specific FOO* to call visit_start,
while using void** lets us use visit_end without a cast. Also,
an upcoming patch will add a clone visitor that wants to use
the same implementation for all three visit_end callbacks,
which is made easier if all three share the same signature.
For visitors with already track per-object state (the QMP visitors
via a stack, and the string visitors which do not allow nesting),
add an assertion that the caller is indeed passing the same
pointer to paired calls.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-4-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:34 +03:00
|
|
|
visit_end_struct(v, NULL);
|
qapi: Add new visit_free() function
Making each visitor provide its own (awkwardly-named) FOO_cleanup()
is unusual, when we can instead have a polymorphic visit_free()
interface. Over the next few patches, we can use the polymorphic
functions to eliminate the need for a FOO_get_visitor() function
for accessing specific visitor functionality, once everything can
be accessed directly through the Visitor* interfaces.
The dealloc visitor is the first one converted to completely use
the new entry point, since qapi_dealloc_visitor_cleanup() was the
only reason that qapi_dealloc_get_visitor() existed, and only
generated and testsuite code was even using it. With the new
visit_free() entry point in place, we no longer need to expose
the QapiDeallocVisitor subtype through qapi_dealloc_visitor_new(),
and can get by with less generated code, with diffs that look like:
| void qapi_free_ACPIOSTInfo(ACPIOSTInfo *obj)
| {
|- QapiDeallocVisitor *qdv;
| Visitor *v;
|
| if (!obj) {
| return;
| }
|
|- qdv = qapi_dealloc_visitor_new();
|- v = qapi_dealloc_get_visitor(qdv);
|+ v = qapi_dealloc_visitor_new();
| visit_type_ACPIOSTInfo(v, NULL, &obj, NULL);
|- qapi_dealloc_visitor_cleanup(qdv);
|+ visit_free(v);
|}
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-5-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:35 +03:00
|
|
|
visit_free(v);
|
2020-04-24 11:43:38 +03:00
|
|
|
''')
|
qapi: check invalid arguments on no-args commands
The generated marshal functions do not visit arguments from commands
that take no arguments. Thus they fail to catch invalid
members. Visit the arguments, if provided, to throw an error in case of
invalid members.
Currently, qmp_check_client_args() checks for invalid arguments and
correctly catches this case. When switching to qmp_dispatch() we want to
keep that behaviour. The commands using 'O' may have arbitrary
arguments, and must have 'gen': false in the qapi schema to skip the
generated checks.
Old/new diff:
void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
+ Visitor *v = NULL;
- (void)args;
+ if (args) {
+ v = qmp_input_visitor_new(QOBJECT(args), true);
+ visit_start_struct(v, NULL, NULL, 0, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+ visit_end_struct(v, NULL);
+ if (err) {
+ goto out;
+ }
+ }
qmp_stop(&err);
+
+out:
error_propagate(errp, err);
+ visit_free(v);
+ if (args) {
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ }
}
The new code closely resembles code for a command with arguments.
Differences:
- the visit of the argument and its cleanup struct don't visit any
members (because there are none).
- the visit of the argument struct and its cleanup are conditional.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 12:19:08 +03:00
|
|
|
|
2015-06-27 18:49:34 +03:00
|
|
|
ret += mcgen('''
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 19:25:50 +03:00
|
|
|
}
|
2015-06-27 18:49:34 +03:00
|
|
|
''')
|
2011-07-19 23:50:42 +04:00
|
|
|
return ret
|
|
|
|
|
2015-09-16 14:06:16 +03:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def gen_register_command(name: str,
|
2021-03-18 18:55:17 +03:00
|
|
|
features: List[QAPISchemaFeature],
|
2020-10-09 19:15:44 +03:00
|
|
|
success_response: bool,
|
|
|
|
allow_oob: bool,
|
|
|
|
allow_preconfig: bool,
|
|
|
|
coroutine: bool) -> str:
|
2018-03-09 12:00:00 +03:00
|
|
|
options = []
|
|
|
|
|
2015-09-16 14:06:11 +03:00
|
|
|
if not success_response:
|
2018-03-09 12:00:00 +03:00
|
|
|
options += ['QCO_NO_SUCCESS_RESP']
|
|
|
|
if allow_oob:
|
|
|
|
options += ['QCO_ALLOW_OOB']
|
2018-05-11 19:51:43 +03:00
|
|
|
if allow_preconfig:
|
|
|
|
options += ['QCO_ALLOW_PRECONFIG']
|
2020-10-05 18:58:49 +03:00
|
|
|
if coroutine:
|
|
|
|
options += ['QCO_COROUTINE']
|
2018-03-09 12:00:00 +03:00
|
|
|
|
2015-09-16 14:06:11 +03:00
|
|
|
ret = mcgen('''
|
2017-03-15 15:57:35 +03:00
|
|
|
qmp_register_command(cmds, "%(name)s",
|
2021-10-28 13:25:17 +03:00
|
|
|
qmp_marshal_%(c_name)s, %(opts)s, %(feats)s);
|
2011-07-19 23:50:42 +04:00
|
|
|
''',
|
2015-09-16 14:06:16 +03:00
|
|
|
name=name, c_name=c_name(name),
|
2021-10-28 13:25:17 +03:00
|
|
|
opts=' | '.join(options) or 0,
|
|
|
|
feats=gen_special_features(features))
|
2015-09-16 14:06:11 +03:00
|
|
|
return ret
|
|
|
|
|
2015-09-16 14:06:16 +03:00
|
|
|
|
qapi: Generate separate .h, .c for each module
Our qapi-schema.json is composed of modules connected by include
directives, but the generated code is monolithic all the same: one
qapi-types.h with all the types, one qapi-visit.h with all the
visitors, and so forth. These monolithic headers get included all
over the place. In my "build everything" tree, adding a QAPI type
recompiles about 4800 out of 5100 objects.
We wouldn't write such monolithic headers by hand. It stands to
reason that we shouldn't generate them, either.
Split up generated qapi-types.h to mirror the schema's modular
structure: one header per module. Name the main module's header
qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
Mirror the schema's includes in the headers, so that qapi-types.h gets
you everything exactly as before. If you need less, you can include
one or more of the sub-module headers. To be exploited shortly.
Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
qmp-commands.c, qapi-event.h, qapi-event.c the same way.
qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.
The split of qmp-commands.c duplicates static helper function
qmp_marshal_output_str() in qapi-commands-char.c and
qapi-commands-misc.c. This happens when commands returning the same
type occur in multiple modules. Not worth avoiding.
Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
already, to reduce churn. This requires temporary hacks in
commands.py and events.py. Similarly, c_name() must temporarily
be taught to munge '/' in common.py. They'll go away with the rename.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180211093607.27351-23-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: declare a dummy variable in each .c file, to shut up OSX
toolchain warnings about empty .o files, including hacking c_name()]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 12:36:00 +03:00
|
|
|
class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
2022-01-26 19:11:26 +03:00
|
|
|
def __init__(self, prefix: str, gen_tracing: bool):
|
2020-03-04 18:59:31 +03:00
|
|
|
super().__init__(
|
|
|
|
prefix, 'qapi-commands',
|
2022-01-26 19:11:26 +03:00
|
|
|
' * Schema-defined QAPI/QMP commands', None, __doc__,
|
|
|
|
gen_tracing=gen_tracing)
|
2020-10-09 19:15:44 +03:00
|
|
|
self._visited_ret_types: Dict[QAPIGenC, Set[QAPISchemaType]] = {}
|
2022-01-26 19:11:26 +03:00
|
|
|
self._gen_tracing = gen_tracing
|
qapi: Generate separate .h, .c for each module
Our qapi-schema.json is composed of modules connected by include
directives, but the generated code is monolithic all the same: one
qapi-types.h with all the types, one qapi-visit.h with all the
visitors, and so forth. These monolithic headers get included all
over the place. In my "build everything" tree, adding a QAPI type
recompiles about 4800 out of 5100 objects.
We wouldn't write such monolithic headers by hand. It stands to
reason that we shouldn't generate them, either.
Split up generated qapi-types.h to mirror the schema's modular
structure: one header per module. Name the main module's header
qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
Mirror the schema's includes in the headers, so that qapi-types.h gets
you everything exactly as before. If you need less, you can include
one or more of the sub-module headers. To be exploited shortly.
Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
qmp-commands.c, qapi-event.h, qapi-event.c the same way.
qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.
The split of qmp-commands.c duplicates static helper function
qmp_marshal_output_str() in qapi-commands-char.c and
qapi-commands-misc.c. This happens when commands returning the same
type occur in multiple modules. Not worth avoiding.
Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
already, to reduce churn. This requires temporary hacks in
commands.py and events.py. Similarly, c_name() must temporarily
be taught to munge '/' in common.py. They'll go away with the rename.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180211093607.27351-23-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: declare a dummy variable in each .c file, to shut up OSX
toolchain warnings about empty .o files, including hacking c_name()]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 12:36:00 +03:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def _begin_user_module(self, name: str) -> None:
|
qapi: Generate separate .h, .c for each module
Our qapi-schema.json is composed of modules connected by include
directives, but the generated code is monolithic all the same: one
qapi-types.h with all the types, one qapi-visit.h with all the
visitors, and so forth. These monolithic headers get included all
over the place. In my "build everything" tree, adding a QAPI type
recompiles about 4800 out of 5100 objects.
We wouldn't write such monolithic headers by hand. It stands to
reason that we shouldn't generate them, either.
Split up generated qapi-types.h to mirror the schema's modular
structure: one header per module. Name the main module's header
qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
Mirror the schema's includes in the headers, so that qapi-types.h gets
you everything exactly as before. If you need less, you can include
one or more of the sub-module headers. To be exploited shortly.
Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
qmp-commands.c, qapi-event.h, qapi-event.c the same way.
qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.
The split of qmp-commands.c duplicates static helper function
qmp_marshal_output_str() in qapi-commands-char.c and
qapi-commands-misc.c. This happens when commands returning the same
type occur in multiple modules. Not worth avoiding.
Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
already, to reduce churn. This requires temporary hacks in
commands.py and events.py. Similarly, c_name() must temporarily
be taught to munge '/' in common.py. They'll go away with the rename.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180211093607.27351-23-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: declare a dummy variable in each .c file, to shut up OSX
toolchain warnings about empty .o files, including hacking c_name()]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 12:36:00 +03:00
|
|
|
self._visited_ret_types[self._genc] = set()
|
2018-02-11 12:36:01 +03:00
|
|
|
commands = self._module_basename('qapi-commands', name)
|
|
|
|
types = self._module_basename('qapi-types', name)
|
|
|
|
visit = self._module_basename('qapi-visit', name)
|
2018-02-26 22:50:08 +03:00
|
|
|
self._genc.add(mcgen('''
|
2016-02-08 18:36:46 +03:00
|
|
|
#include "qemu/osdep.h"
|
qapi: Implement deprecated-output=hide for QMP command results
This policy suppresses deprecated bits in output, and thus permits
"testing the future". Implement it for QMP command results. Example:
when QEMU is run with -compat deprecated-output=hide, then
{"execute": "query-cpus-fast"}
yields
{"return": [{"thread-id": 9805, "props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "qom-path": "/machine/unattached/device[0]", "cpu-index": 0, "target": "x86_64"}]}
instead of
{"return": [{"arch": "x86", "thread-id": 22436, "props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "qom-path": "/machine/unattached/device[0]", "cpu-index": 0, "target": "x86_64"}]}
Note the suppression of deprecated member "arch".
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210318155519.1224118-4-armbru@redhat.com>
2021-03-18 18:55:11 +03:00
|
|
|
#include "qapi/compat-policy.h"
|
2015-04-02 15:52:55 +03:00
|
|
|
#include "qapi/visitor.h"
|
2018-02-01 14:18:39 +03:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2015-04-02 15:52:55 +03:00
|
|
|
#include "qapi/dealloc-visitor.h"
|
2018-02-01 14:18:31 +03:00
|
|
|
#include "qapi/error.h"
|
2018-02-11 12:36:01 +03:00
|
|
|
#include "%(visit)s.h"
|
|
|
|
#include "%(commands)s.h"
|
2015-04-02 15:52:55 +03:00
|
|
|
''',
|
2018-02-11 12:36:01 +03:00
|
|
|
commands=commands, visit=visit))
|
2022-01-26 19:11:26 +03:00
|
|
|
|
|
|
|
if self._gen_tracing and commands != 'qapi-commands':
|
|
|
|
self._genc.add(mcgen('''
|
|
|
|
#include "qapi/qmp/qjson.h"
|
|
|
|
#include "trace/trace-%(nm)s_trace_events.h"
|
|
|
|
''',
|
|
|
|
nm=c_name(commands, protect=False)))
|
|
|
|
# We use c_name(commands, protect=False) to turn '-' into '_', to
|
|
|
|
# match .underscorify() in trace/meson.build
|
|
|
|
|
2018-02-26 22:50:08 +03:00
|
|
|
self._genh.add(mcgen('''
|
2018-02-11 12:36:01 +03:00
|
|
|
#include "%(types)s.h"
|
2015-04-02 15:52:55 +03:00
|
|
|
|
|
|
|
''',
|
2018-02-11 12:36:01 +03:00
|
|
|
types=types))
|
2018-02-26 22:50:08 +03:00
|
|
|
|
2021-02-01 22:37:44 +03:00
|
|
|
def visit_begin(self, schema: QAPISchema) -> None:
|
2021-02-01 22:37:40 +03:00
|
|
|
self._add_module('./init', ' * QAPI Commands initialization')
|
2019-11-20 21:25:48 +03:00
|
|
|
self._genh.add(mcgen('''
|
|
|
|
#include "qapi/qmp/dispatch.h"
|
|
|
|
|
qapi: Generate separate .h, .c for each module
Our qapi-schema.json is composed of modules connected by include
directives, but the generated code is monolithic all the same: one
qapi-types.h with all the types, one qapi-visit.h with all the
visitors, and so forth. These monolithic headers get included all
over the place. In my "build everything" tree, adding a QAPI type
recompiles about 4800 out of 5100 objects.
We wouldn't write such monolithic headers by hand. It stands to
reason that we shouldn't generate them, either.
Split up generated qapi-types.h to mirror the schema's modular
structure: one header per module. Name the main module's header
qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
Mirror the schema's includes in the headers, so that qapi-types.h gets
you everything exactly as before. If you need less, you can include
one or more of the sub-module headers. To be exploited shortly.
Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
qmp-commands.c, qapi-event.h, qapi-event.c the same way.
qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.
The split of qmp-commands.c duplicates static helper function
qmp_marshal_output_str() in qapi-commands-char.c and
qapi-commands-misc.c. This happens when commands returning the same
type occur in multiple modules. Not worth avoiding.
Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
already, to reduce churn. This requires temporary hacks in
commands.py and events.py. Similarly, c_name() must temporarily
be taught to munge '/' in common.py. They'll go away with the rename.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180211093607.27351-23-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: declare a dummy variable in each .c file, to shut up OSX
toolchain warnings about empty .o files, including hacking c_name()]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 12:36:00 +03:00
|
|
|
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
|
|
|
|
''',
|
2020-03-04 18:59:32 +03:00
|
|
|
c_prefix=c_name(self._prefix, protect=False)))
|
2021-02-01 22:37:44 +03:00
|
|
|
self._genc.add(mcgen('''
|
2019-11-20 21:25:48 +03:00
|
|
|
#include "qemu/osdep.h"
|
|
|
|
#include "%(prefix)sqapi-commands.h"
|
|
|
|
#include "%(prefix)sqapi-init-commands.h"
|
2021-02-01 22:37:44 +03:00
|
|
|
|
|
|
|
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
|
|
|
{
|
|
|
|
QTAILQ_INIT(cmds);
|
|
|
|
|
2019-11-20 21:25:48 +03:00
|
|
|
''',
|
2021-02-01 22:37:44 +03:00
|
|
|
prefix=self._prefix,
|
|
|
|
c_prefix=c_name(self._prefix, protect=False)))
|
|
|
|
|
|
|
|
def visit_end(self) -> None:
|
|
|
|
with self._temp_module('./init'):
|
|
|
|
self._genc.add(mcgen('''
|
|
|
|
}
|
|
|
|
'''))
|
2018-02-26 22:50:08 +03:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def visit_command(self,
|
|
|
|
name: str,
|
2021-02-01 22:37:46 +03:00
|
|
|
info: Optional[QAPISourceInfo],
|
2021-08-04 11:30:57 +03:00
|
|
|
ifcond: QAPISchemaIfCond,
|
2020-10-09 19:15:44 +03:00
|
|
|
features: List[QAPISchemaFeature],
|
|
|
|
arg_type: Optional[QAPISchemaObjectType],
|
|
|
|
ret_type: Optional[QAPISchemaType],
|
|
|
|
gen: bool,
|
|
|
|
success_response: bool,
|
|
|
|
boxed: bool,
|
|
|
|
allow_oob: bool,
|
|
|
|
allow_preconfig: bool,
|
|
|
|
coroutine: bool) -> None:
|
2018-02-26 22:50:08 +03:00
|
|
|
if not gen:
|
|
|
|
return
|
2018-07-03 18:56:43 +03:00
|
|
|
# FIXME: If T is a user-defined type, the user is responsible
|
|
|
|
# for making this work, i.e. to make T's condition the
|
|
|
|
# conjunction of the T-returning commands' conditions. If T
|
|
|
|
# is a built-in type, this isn't possible: the
|
|
|
|
# qmp_marshal_output_T() will be generated unconditionally.
|
qapi: Generate separate .h, .c for each module
Our qapi-schema.json is composed of modules connected by include
directives, but the generated code is monolithic all the same: one
qapi-types.h with all the types, one qapi-visit.h with all the
visitors, and so forth. These monolithic headers get included all
over the place. In my "build everything" tree, adding a QAPI type
recompiles about 4800 out of 5100 objects.
We wouldn't write such monolithic headers by hand. It stands to
reason that we shouldn't generate them, either.
Split up generated qapi-types.h to mirror the schema's modular
structure: one header per module. Name the main module's header
qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h.
Mirror the schema's includes in the headers, so that qapi-types.h gets
you everything exactly as before. If you need less, you can include
one or more of the sub-module headers. To be exploited shortly.
Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h,
qmp-commands.c, qapi-event.h, qapi-event.c the same way.
qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic.
The split of qmp-commands.c duplicates static helper function
qmp_marshal_output_str() in qapi-commands-char.c and
qapi-commands-misc.c. This happens when commands returning the same
type occur in multiple modules. Not worth avoiding.
Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and
qmp-commands.[ch] to qapi-commands.[ch], name the shards that way
already, to reduce churn. This requires temporary hacks in
commands.py and events.py. Similarly, c_name() must temporarily
be taught to munge '/' in common.py. They'll go away with the rename.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180211093607.27351-23-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: declare a dummy variable in each .c file, to shut up OSX
toolchain warnings about empty .o files, including hacking c_name()]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 12:36:00 +03:00
|
|
|
if ret_type and ret_type not in self._visited_ret_types[self._genc]:
|
|
|
|
self._visited_ret_types[self._genc].add(ret_type)
|
2018-07-03 18:56:43 +03:00
|
|
|
with ifcontext(ret_type.ifcond,
|
2021-02-01 22:37:44 +03:00
|
|
|
self._genh, self._genc):
|
2018-07-03 18:56:43 +03:00
|
|
|
self._genc.add(gen_marshal_output(ret_type))
|
2021-02-01 22:37:44 +03:00
|
|
|
with ifcontext(ifcond, self._genh, self._genc):
|
2022-10-13 11:50:49 +03:00
|
|
|
self._genh.add(gen_command_decl(name, arg_type, boxed,
|
|
|
|
ret_type, coroutine))
|
|
|
|
self._genh.add(gen_marshal_decl(name, coroutine))
|
2022-01-26 19:11:26 +03:00
|
|
|
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type,
|
2022-10-13 11:50:49 +03:00
|
|
|
self._gen_tracing, coroutine))
|
2022-01-26 19:11:26 +03:00
|
|
|
if self._gen_tracing:
|
|
|
|
self._gen_trace_events.add(gen_trace(name))
|
2021-02-01 22:37:44 +03:00
|
|
|
with self._temp_module('./init'):
|
|
|
|
with ifcontext(ifcond, self._genh, self._genc):
|
2021-03-18 18:55:17 +03:00
|
|
|
self._genc.add(gen_register_command(
|
|
|
|
name, features, success_response, allow_oob,
|
|
|
|
allow_preconfig, coroutine))
|
2018-02-26 22:50:08 +03:00
|
|
|
|
2018-02-26 22:39:37 +03:00
|
|
|
|
2020-10-09 19:15:44 +03:00
|
|
|
def gen_commands(schema: QAPISchema,
|
|
|
|
output_dir: str,
|
2022-01-26 19:11:26 +03:00
|
|
|
prefix: str,
|
|
|
|
gen_tracing: bool) -> None:
|
|
|
|
vis = QAPISchemaGenCommandVisitor(prefix, gen_tracing)
|
2018-02-26 22:39:37 +03:00
|
|
|
schema.visit(vis)
|
2018-02-26 22:50:08 +03:00
|
|
|
vis.write(output_dir)
|