unicorn/qemu/scripts/qapi-event.py

232 lines
5.6 KiB
Python
Raw Normal View History

2015-08-21 07:04:50 +00:00
#
# QAPI event generator
#
# Copyright (c) 2014 Wenchao Xia
qapi: Improve generated event use of qapi visitor All other successful clients of visit_start_struct() were paired with an unconditional visit_end_struct(); but the generated code for events was relying on qmp_output_visitor_cleanup() to work on an incomplete visit. Alter the code to guarantee that the struct is completed, which will make a future patch to split visit_end_struct() easier to reason about. While at it, drop some assertions and comments that are not present in other uses of the qmp output visitor, and pass NULL rather than "" as the 'kind' parameter (matching most other uses where obj is NULL). The changes to the generated code look like: | qmp = qmp_event_build_dict("DEVICE_TRAY_MOVED"); | | qov = qmp_output_visitor_new(); |- g_assert(qov); |- | v = qmp_output_get_visitor(qov); |- g_assert(v); | |- /* Fake visit, as if all members are under a structure */ |- visit_start_struct(v, NULL, "", "DEVICE_TRAY_MOVED", 0, &err); |+ visit_start_struct(v, NULL, NULL, "DEVICE_TRAY_MOVED", 0, &err); | if (err) { | goto out; | } | visit_type_str(v, (char **)&device, "device", &err); | if (err) { |- goto out; |+ goto out_obj; | } | visit_type_bool(v, &tray_open, "tray-open", &err); | if (err) { |- goto out; |+ goto out_obj; | } |- visit_end_struct(v, &err); |+out_obj: |+ visit_end_struct(v, err ? NULL : &err); | if (err) { | goto out; | } | | obj = qmp_output_get_qobject(qov); |- g_assert(obj != NULL); |+ g_assert(obj); | | qdict_put_obj(qmp, "data", obj); | emit(QAPI_EVENT_DEVICE_TRAY_MOVED, qmp, &err); Note that the 'goto out_obj' with no intervening code before the label, as well as the construct of 'err ? NULL : &err', are both a bit unusual but also temporary; they get fixed in a later patch that splits visit_end_struct() to drop its errp parameter by moving some checking before the label. But until that time, this was the simplest way to avoid the appearance of passing a possibly-set error to visit_end_struct(), even though actual code inspection shows that visit_end_struct() for a QMP output visitor will never set an error. Backports commit a16e3e5c5825c90887a863513916f93eeec16c55 from qemu
2018-02-20 03:17:42 +00:00
# Copyright (c) 2015-2016 Red Hat Inc.
2015-08-21 07:04:50 +00:00
#
# Authors:
# Wenchao Xia <wenchaoqemu@gmail.com>
# Markus Armbruster <armbru@redhat.com>
2015-08-21 07:04:50 +00:00
#
# 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 *
def gen_event_send_proto(name, arg_type, boxed):
return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
'c_name': c_name(name.lower()),
'param': gen_params(arg_type, boxed, 'Error **errp')}
2015-08-21 07:04:50 +00:00
def gen_event_send_decl(name, arg_type, boxed):
2015-08-21 07:04:50 +00:00
return mcgen('''
%(proto)s;
2015-08-21 07:04:50 +00:00
''',
proto=gen_event_send_proto(name, arg_type, boxed))
2015-08-21 07:04:50 +00:00
qapi-event: Utilize implicit struct visits Rather than generate inline per-member visits, take advantage of the 'visit_type_FOO_members()' function for emitting events. This is possible now that implicit structs can be visited like any other. Generated code shrinks accordingly; by initializing a struct based on parameters, through a new gen_param_var() helper, like: |@@ -338,6 +250,9 @@ void qapi_event_send_block_job_error(con | QMPEventFuncEmit emit = qmp_event_get_func_emit(); | QmpOutputVisitor *qov; | Visitor *v; |+ q_obj_BLOCK_JOB_ERROR_arg param = { |+ (char *)device, operation, action |+ }; | | if (!emit) { | return; @@ -351,19 +266,7 @@ void qapi_event_send_block_job_error(con | if (err) { | goto out; | } |- visit_type_str(v, "device", (char **)&device, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_IoOperationType(v, "operation", &operation, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_BlockErrorAction(v, "action", &action, &err); |- if (err) { |- goto out_obj; |- } |-out_obj: |+ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, &param, &err); | visit_end_struct(v, err ? NULL : &err); Notice that the initialization of 'param' has to cast away const (just as the old gen_visit_members() had to do): we can't change the signature of the user function (which uses 'const char *'), but have to assign it to a non-const QAPI object (which requires 'char *'). While touching this, document with a FIXME comment that there is still a potential collision between QMP members and our choice of local variable names within qapi_event_send_FOO(). This patch also paves the way for some followup simplifications in the generator, in subsequent patches. Backports commit 0949e95b48e30715e157cabbc59dcb0ed912d3ff from qemu
2018-02-22 03:45:26 +00:00
# Declare and initialize an object 'qapi' using parameters from gen_params()
def gen_param_var(typ):
assert not typ.variants
ret = mcgen('''
%(c_name)s param = {
''',
c_name=typ.c_name())
sep = ' '
for memb in typ.members:
ret += sep
sep = ', '
if memb.optional:
ret += 'has_' + c_name(memb.name) + sep
if memb.type.name == 'str':
# Cast away const added in gen_params()
ret += '(char *)'
ret += c_name(memb.name)
ret += mcgen('''
};
''')
qapi-event: Simplify visit of non-implicit data Commit 7ce106a9 documented why we don't generated a visit_type_FOO() for implicit types; and therefore events with an anonymous type for 'data' have to open-code a visit. Note that the open-coded visit in qapi-event.c is slightly different from what is done in qapi-visit.c for normal types, in part because we don't have to check for *obj being NULL or free things on error. But where the type is not implicit, it is nicer to reuse the normal visit instead of open-coding a duplicate. At the moment, the only event with a non-implicit 'data' is in the testsuite, where test-qapi-event.c changes as follows: |@@ -155,6 +155,7 @@ void qapi_event_send___org_qemu_x_event( | __org_qemu_x_Struct param = { | __org_qemu_x_member1, (char *)__org_qemu_x_member2, has_q_wchar_t, q_wchar_t | }; |+ __org_qemu_x_Struct *arg = &param; | | emit = qmp_event_get_func_emit(); | if (!emit) { |@@ -164,16 +165,7 @@ void qapi_event_send___org_qemu_x_event( | qmp = qmp_event_build_dict("__ORG.QEMU_X-EVENT"); | | v = qmp_output_visitor_new(&obj); |- |- visit_start_struct(v, "__ORG.QEMU_X-EVENT", NULL, 0, &err); |- if (err) { |- goto out; |- } |- visit_type___org_qemu_x_Struct_members(v, &param, &err); |- if (!err) { |- if (!err) { |- visit_check_struct(v, &err); |- } |- visit_end_struct(v, NULL); |+ visit_type___org_qemu_x_Struct(v, "__ORG.QEMU_X-EVENT", &arg, &err); | if (err) { | goto out; | } Backports commit 4d0b268fdb17a1fed10fe980e77fd388e5427bfd from qemu
2018-02-26 01:12:32 +00:00
if not typ.is_implicit():
ret += mcgen('''
%(c_name)s *arg = &param;
''',
c_name=typ.c_name())
qapi-event: Utilize implicit struct visits Rather than generate inline per-member visits, take advantage of the 'visit_type_FOO_members()' function for emitting events. This is possible now that implicit structs can be visited like any other. Generated code shrinks accordingly; by initializing a struct based on parameters, through a new gen_param_var() helper, like: |@@ -338,6 +250,9 @@ void qapi_event_send_block_job_error(con | QMPEventFuncEmit emit = qmp_event_get_func_emit(); | QmpOutputVisitor *qov; | Visitor *v; |+ q_obj_BLOCK_JOB_ERROR_arg param = { |+ (char *)device, operation, action |+ }; | | if (!emit) { | return; @@ -351,19 +266,7 @@ void qapi_event_send_block_job_error(con | if (err) { | goto out; | } |- visit_type_str(v, "device", (char **)&device, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_IoOperationType(v, "operation", &operation, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_BlockErrorAction(v, "action", &action, &err); |- if (err) { |- goto out_obj; |- } |-out_obj: |+ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, &param, &err); | visit_end_struct(v, err ? NULL : &err); Notice that the initialization of 'param' has to cast away const (just as the old gen_visit_members() had to do): we can't change the signature of the user function (which uses 'const char *'), but have to assign it to a non-const QAPI object (which requires 'char *'). While touching this, document with a FIXME comment that there is still a potential collision between QMP members and our choice of local variable names within qapi_event_send_FOO(). This patch also paves the way for some followup simplifications in the generator, in subsequent patches. Backports commit 0949e95b48e30715e157cabbc59dcb0ed912d3ff from qemu
2018-02-22 03:45:26 +00:00
return ret
def gen_event_send(name, arg_type, boxed):
qapi-event: Utilize implicit struct visits Rather than generate inline per-member visits, take advantage of the 'visit_type_FOO_members()' function for emitting events. This is possible now that implicit structs can be visited like any other. Generated code shrinks accordingly; by initializing a struct based on parameters, through a new gen_param_var() helper, like: |@@ -338,6 +250,9 @@ void qapi_event_send_block_job_error(con | QMPEventFuncEmit emit = qmp_event_get_func_emit(); | QmpOutputVisitor *qov; | Visitor *v; |+ q_obj_BLOCK_JOB_ERROR_arg param = { |+ (char *)device, operation, action |+ }; | | if (!emit) { | return; @@ -351,19 +266,7 @@ void qapi_event_send_block_job_error(con | if (err) { | goto out; | } |- visit_type_str(v, "device", (char **)&device, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_IoOperationType(v, "operation", &operation, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_BlockErrorAction(v, "action", &action, &err); |- if (err) { |- goto out_obj; |- } |-out_obj: |+ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, &param, &err); | visit_end_struct(v, err ? NULL : &err); Notice that the initialization of 'param' has to cast away const (just as the old gen_visit_members() had to do): we can't change the signature of the user function (which uses 'const char *'), but have to assign it to a non-const QAPI object (which requires 'char *'). While touching this, document with a FIXME comment that there is still a potential collision between QMP members and our choice of local variable names within qapi_event_send_FOO(). This patch also paves the way for some followup simplifications in the generator, in subsequent patches. Backports commit 0949e95b48e30715e157cabbc59dcb0ed912d3ff from qemu
2018-02-22 03:45:26 +00:00
# FIXME: Our declaration of local variables (and of 'errp' in the
# parameter list) can collide with exploded members of the event's
# data type passed in as parameters. If this collision ever hits in
# practice, we can rename our local variables with a leading _ prefix,
# or split the code into a wrapper function that creates a boxed
# 'param' object then calls another to do the real work.
ret = mcgen('''
2015-08-21 07:04:50 +00:00
%(proto)s
2015-08-21 07:04:50 +00:00
{
QDict *qmp;
Error *err = NULL;
2015-08-21 07:04:50 +00:00
QMPEventFuncEmit emit;
''',
proto=gen_event_send_proto(name, arg_type, boxed))
2015-08-21 07:04:50 +00:00
if arg_type and not arg_type.is_empty():
assert not arg_type.variants
ret += mcgen('''
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); Backports commit 3b098d56979d2f7fd707c5be85555d114353a28d from qemu
2018-02-25 06:20:00 +00:00
QObject *obj;
2015-08-21 07:04:50 +00:00
Visitor *v;
''')
qapi: Implement boxed types for commands/events Turn on the ability to pass command and event arguments in a single boxed parameter, which must name a non-empty type (although the type can be a struct with all optional members). For structs, it makes it possible to pass a single qapi type instead of a breakout of all struct members (useful if the arguments are already in a struct or if the number of members is large); for other complex types, it is now possible to use a union or alternate as the data for a command or event. The empty type may be technically feasible if needed down the road, but it's easier to forbid it now and relax things to allow it later, than it is to allow it now and have to special case how the generated 'q_empty' type is handled (see commit 7ce106a9 for reasons why nothing is generated for the empty type). An alternate type is never considered empty, but now that a boxed type can be either an object or an alternate, we have to provide a trivial QAPISchemaAlternateType.is_empty(). The new call to arg_type.is_empty() during QAPISchemaCommand.check() requires that we first check the type in question; but there is no chance of introducing a cycle since objects do not refer back to commands. We still have a split in syntax checking between ad-hoc parsing up front (merely validates that 'boxed' has a sane value) and during .check() methods (if 'boxed' is set, then 'data' must name a non-empty user-defined type). Generated code is unchanged, as long as no client uses the new feature. Backports commit c818408e449ea55371253bd4def1c1dc87b7bb03 from qemu
2018-02-26 01:22:00 +00:00
if not boxed:
ret += gen_param_var(arg_type)
else:
assert not boxed
2015-08-21 07:04:50 +00:00
ret += mcgen('''
qapi-event: Utilize implicit struct visits Rather than generate inline per-member visits, take advantage of the 'visit_type_FOO_members()' function for emitting events. This is possible now that implicit structs can be visited like any other. Generated code shrinks accordingly; by initializing a struct based on parameters, through a new gen_param_var() helper, like: |@@ -338,6 +250,9 @@ void qapi_event_send_block_job_error(con | QMPEventFuncEmit emit = qmp_event_get_func_emit(); | QmpOutputVisitor *qov; | Visitor *v; |+ q_obj_BLOCK_JOB_ERROR_arg param = { |+ (char *)device, operation, action |+ }; | | if (!emit) { | return; @@ -351,19 +266,7 @@ void qapi_event_send_block_job_error(con | if (err) { | goto out; | } |- visit_type_str(v, "device", (char **)&device, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_IoOperationType(v, "operation", &operation, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_BlockErrorAction(v, "action", &action, &err); |- if (err) { |- goto out_obj; |- } |-out_obj: |+ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, &param, &err); | visit_end_struct(v, err ? NULL : &err); Notice that the initialization of 'param' has to cast away const (just as the old gen_visit_members() had to do): we can't change the signature of the user function (which uses 'const char *'), but have to assign it to a non-const QAPI object (which requires 'char *'). While touching this, document with a FIXME comment that there is still a potential collision between QMP members and our choice of local variable names within qapi_event_send_FOO(). This patch also paves the way for some followup simplifications in the generator, in subsequent patches. Backports commit 0949e95b48e30715e157cabbc59dcb0ed912d3ff from qemu
2018-02-22 03:45:26 +00:00
2015-08-21 07:04:50 +00:00
emit = qmp_event_get_func_emit();
if (!emit) {
return;
}
qmp = qmp_event_build_dict("%(name)s");
2015-08-21 07:04:50 +00:00
''',
name=name)
2015-08-21 07:04:50 +00:00
if arg_type and not arg_type.is_empty():
ret += mcgen('''
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); Backports commit 3b098d56979d2f7fd707c5be85555d114353a28d from qemu
2018-02-25 06:20:00 +00:00
v = qmp_output_visitor_new(&obj);
qapi-event: Simplify visit of non-implicit data Commit 7ce106a9 documented why we don't generated a visit_type_FOO() for implicit types; and therefore events with an anonymous type for 'data' have to open-code a visit. Note that the open-coded visit in qapi-event.c is slightly different from what is done in qapi-visit.c for normal types, in part because we don't have to check for *obj being NULL or free things on error. But where the type is not implicit, it is nicer to reuse the normal visit instead of open-coding a duplicate. At the moment, the only event with a non-implicit 'data' is in the testsuite, where test-qapi-event.c changes as follows: |@@ -155,6 +155,7 @@ void qapi_event_send___org_qemu_x_event( | __org_qemu_x_Struct param = { | __org_qemu_x_member1, (char *)__org_qemu_x_member2, has_q_wchar_t, q_wchar_t | }; |+ __org_qemu_x_Struct *arg = &param; | | emit = qmp_event_get_func_emit(); | if (!emit) { |@@ -164,16 +165,7 @@ void qapi_event_send___org_qemu_x_event( | qmp = qmp_event_build_dict("__ORG.QEMU_X-EVENT"); | | v = qmp_output_visitor_new(&obj); |- |- visit_start_struct(v, "__ORG.QEMU_X-EVENT", NULL, 0, &err); |- if (err) { |- goto out; |- } |- visit_type___org_qemu_x_Struct_members(v, &param, &err); |- if (!err) { |- if (!err) { |- visit_check_struct(v, &err); |- } |- visit_end_struct(v, NULL); |+ visit_type___org_qemu_x_Struct(v, "__ORG.QEMU_X-EVENT", &arg, &err); | if (err) { | goto out; | } Backports commit 4d0b268fdb17a1fed10fe980e77fd388e5427bfd from qemu
2018-02-26 01:12:32 +00:00
''')
if not arg_type.is_implicit():
ret += mcgen('''
visit_type_%(c_name)s(v, "%(name)s", &arg, &err);
''',
name=name, c_name=arg_type.c_name())
else:
ret += mcgen('''
2015-08-21 07:04:50 +00:00
visit_start_struct(v, "%(name)s", NULL, 0, &err);
qapi-event: Utilize implicit struct visits Rather than generate inline per-member visits, take advantage of the 'visit_type_FOO_members()' function for emitting events. This is possible now that implicit structs can be visited like any other. Generated code shrinks accordingly; by initializing a struct based on parameters, through a new gen_param_var() helper, like: |@@ -338,6 +250,9 @@ void qapi_event_send_block_job_error(con | QMPEventFuncEmit emit = qmp_event_get_func_emit(); | QmpOutputVisitor *qov; | Visitor *v; |+ q_obj_BLOCK_JOB_ERROR_arg param = { |+ (char *)device, operation, action |+ }; | | if (!emit) { | return; @@ -351,19 +266,7 @@ void qapi_event_send_block_job_error(con | if (err) { | goto out; | } |- visit_type_str(v, "device", (char **)&device, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_IoOperationType(v, "operation", &operation, &err); |- if (err) { |- goto out_obj; |- } |- visit_type_BlockErrorAction(v, "action", &action, &err); |- if (err) { |- goto out_obj; |- } |-out_obj: |+ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, &param, &err); | visit_end_struct(v, err ? NULL : &err); Notice that the initialization of 'param' has to cast away const (just as the old gen_visit_members() had to do): we can't change the signature of the user function (which uses 'const char *'), but have to assign it to a non-const QAPI object (which requires 'char *'). While touching this, document with a FIXME comment that there is still a potential collision between QMP members and our choice of local variable names within qapi_event_send_FOO(). This patch also paves the way for some followup simplifications in the generator, in subsequent patches. Backports commit 0949e95b48e30715e157cabbc59dcb0ed912d3ff from qemu
2018-02-22 03:45:26 +00:00
if (err) {
goto out;
}
visit_type_%(c_name)s_members(v, &param, &err);
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, &param, &err); |- visit_end_struct(v, err ? NULL : &err); |+ if (!err) { |+ visit_check_struct(v, &err); |+ } |+ visit_end_struct(v); | if (err) { | goto out; Backports commit 15c2f669e3fb2bc97f7b42d1871f595c0ac24af8 from qemu
2018-02-24 00:12:23 +00:00
if (!err) {
visit_check_struct(v, &err);
}
visit_end_struct(v, NULL);
qapi-event: Simplify visit of non-implicit data Commit 7ce106a9 documented why we don't generated a visit_type_FOO() for implicit types; and therefore events with an anonymous type for 'data' have to open-code a visit. Note that the open-coded visit in qapi-event.c is slightly different from what is done in qapi-visit.c for normal types, in part because we don't have to check for *obj being NULL or free things on error. But where the type is not implicit, it is nicer to reuse the normal visit instead of open-coding a duplicate. At the moment, the only event with a non-implicit 'data' is in the testsuite, where test-qapi-event.c changes as follows: |@@ -155,6 +155,7 @@ void qapi_event_send___org_qemu_x_event( | __org_qemu_x_Struct param = { | __org_qemu_x_member1, (char *)__org_qemu_x_member2, has_q_wchar_t, q_wchar_t | }; |+ __org_qemu_x_Struct *arg = &param; | | emit = qmp_event_get_func_emit(); | if (!emit) { |@@ -164,16 +165,7 @@ void qapi_event_send___org_qemu_x_event( | qmp = qmp_event_build_dict("__ORG.QEMU_X-EVENT"); | | v = qmp_output_visitor_new(&obj); |- |- visit_start_struct(v, "__ORG.QEMU_X-EVENT", NULL, 0, &err); |- if (err) { |- goto out; |- } |- visit_type___org_qemu_x_Struct_members(v, &param, &err); |- if (!err) { |- if (!err) { |- visit_check_struct(v, &err); |- } |- visit_end_struct(v, NULL); |+ visit_type___org_qemu_x_Struct(v, "__ORG.QEMU_X-EVENT", &arg, &err); | if (err) { | goto out; | } Backports commit 4d0b268fdb17a1fed10fe980e77fd388e5427bfd from qemu
2018-02-26 01:12:32 +00:00
''',
name=name, c_name=arg_type.c_name())
ret += mcgen('''
if (err) {
goto out;
2015-08-21 07:04:50 +00:00
}
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); Backports commit 3b098d56979d2f7fd707c5be85555d114353a28d from qemu
2018-02-25 06:20:00 +00:00
visit_complete(v, &obj);
qdict_put_obj(qmp, "data", obj);
qapi-event: Simplify visit of non-implicit data Commit 7ce106a9 documented why we don't generated a visit_type_FOO() for implicit types; and therefore events with an anonymous type for 'data' have to open-code a visit. Note that the open-coded visit in qapi-event.c is slightly different from what is done in qapi-visit.c for normal types, in part because we don't have to check for *obj being NULL or free things on error. But where the type is not implicit, it is nicer to reuse the normal visit instead of open-coding a duplicate. At the moment, the only event with a non-implicit 'data' is in the testsuite, where test-qapi-event.c changes as follows: |@@ -155,6 +155,7 @@ void qapi_event_send___org_qemu_x_event( | __org_qemu_x_Struct param = { | __org_qemu_x_member1, (char *)__org_qemu_x_member2, has_q_wchar_t, q_wchar_t | }; |+ __org_qemu_x_Struct *arg = &param; | | emit = qmp_event_get_func_emit(); | if (!emit) { |@@ -164,16 +165,7 @@ void qapi_event_send___org_qemu_x_event( | qmp = qmp_event_build_dict("__ORG.QEMU_X-EVENT"); | | v = qmp_output_visitor_new(&obj); |- |- visit_start_struct(v, "__ORG.QEMU_X-EVENT", NULL, 0, &err); |- if (err) { |- goto out; |- } |- visit_type___org_qemu_x_Struct_members(v, &param, &err); |- if (!err) { |- if (!err) { |- visit_check_struct(v, &err); |- } |- visit_end_struct(v, NULL); |+ visit_type___org_qemu_x_Struct(v, "__ORG.QEMU_X-EVENT", &arg, &err); | if (err) { | goto out; | } Backports commit 4d0b268fdb17a1fed10fe980e77fd388e5427bfd from qemu
2018-02-26 01:12:32 +00:00
''')
2015-08-21 07:04:50 +00:00
ret += mcgen('''
emit(%(c_enum)s, qmp, &err);
2015-08-21 07:04:50 +00:00
''',
c_enum=c_enum_const(event_enum_name, name))
2015-08-21 07:04:50 +00:00
if arg_type and not arg_type.is_empty():
ret += mcgen('''
out:
visit_free(v);
''')
ret += mcgen('''
error_propagate(errp, err);
2015-08-21 07:04:50 +00:00
QDECREF(qmp);
}
''')
2015-08-21 07:04:50 +00:00
return ret
class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
def __init__(self):
self.decl = None
self.defn = None
self._event_names = None
def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._event_names = []
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;
def visit_event(self, name, info, arg_type, boxed):
self.decl += gen_event_send_decl(name, arg_type, boxed)
self.defn += gen_event_send(name, arg_type, boxed)
self._event_names.append(name)
(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
2015-08-21 07:04:50 +00:00
c_comment = '''
2015-08-21 07:04:50 +00:00
/*
* schema-defined QAPI event functions
*
* Copyright (c) 2014 Wenchao Xia
*
* Authors:
* Wenchao Xia <wenchaoqemu@gmail.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
'''
h_comment = '''
2015-08-21 07:04:50 +00:00
/*
* schema-defined QAPI event functions
*
* Copyright (c) 2014 Wenchao Xia
*
* Authors:
* Wenchao Xia <wenchaoqemu@gmail.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
'''
2015-08-21 07:04:50 +00:00
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
'qapi-event.c', 'qapi-event.h',
c_comment, h_comment)
fdef.write(mcgen('''
#include "qemu-common.h"
#include "%(prefix)sqapi-event.h"
#include "%(prefix)sqapi-visit.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi/qmp-event.h"
''',
prefix=prefix))
2015-08-21 07:04:50 +00:00
fdecl.write(mcgen('''
2015-08-21 07:04:50 +00:00
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "%(prefix)sqapi-types.h"
''',
prefix=prefix))
2015-08-21 07:04:50 +00:00
event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
schema = QAPISchema(input_file)
gen = QAPISchemaGenEventVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
2015-08-21 07:04:50 +00:00
close_output(fdef, fdecl)