| """ | |
| QAPI event generator | |
| Copyright (c) 2014 Wenchao Xia | |
| Copyright (c) 2015-2018 Red Hat Inc. | |
| Authors: | |
| Wenchao Xia <wenchaoqemu@gmail.com> | |
| Markus Armbruster <armbru@redhat.com> | |
| This work is licensed under the terms of the GNU GPL, version 2. | |
| See the COPYING file in the top-level directory. | |
| """ | |
| from typing import List, Optional | |
| from .common import c_enum_const, c_name, mcgen | |
| from .gen import QAPISchemaModularCVisitor, build_params, ifcontext | |
| from .schema import ( | |
| QAPISchema, | |
| QAPISchemaEnumMember, | |
| QAPISchemaFeature, | |
| QAPISchemaIfCond, | |
| QAPISchemaObjectType, | |
| ) | |
| from .source import QAPISourceInfo | |
| from .types import gen_enum, gen_enum_lookup | |
| def build_event_send_proto(name: str, | |
| arg_type: Optional[QAPISchemaObjectType], | |
| boxed: bool) -> str: | |
| return 'void qapi_event_send_%(c_name)s(%(param)s)' % { | |
| 'c_name': c_name(name.lower()), | |
| 'param': build_params(arg_type, boxed)} | |
| def gen_event_send_decl(name: str, | |
| arg_type: Optional[QAPISchemaObjectType], | |
| boxed: bool) -> str: | |
| return mcgen(''' | |
| %(proto)s; | |
| ''', | |
| proto=build_event_send_proto(name, arg_type, boxed)) | |
| def gen_param_var(typ: QAPISchemaObjectType) -> str: | |
| """ | |
| Generate a struct variable holding the event parameters. | |
| Initialize it with the function arguments defined in `gen_event_send`. | |
| """ | |
| assert not typ.branches | |
| ret = mcgen(''' | |
| %(c_name)s param = { | |
| ''', | |
| c_name=typ.c_name()) | |
| sep = ' ' | |
| for memb in typ.members: | |
| ret += sep | |
| sep = ', ' | |
| if memb.need_has(): | |
| ret += 'has_' + c_name(memb.name) + sep | |
| if memb.type.name == 'str': | |
| # Cast away const added in build_params() | |
| ret += '(char *)' | |
| ret += c_name(memb.name) | |
| ret += mcgen(''' | |
| }; | |
| ''') | |
| if not typ.is_implicit(): | |
| ret += mcgen(''' | |
| %(c_name)s *arg = ¶m; | |
| ''', | |
| c_name=typ.c_name()) | |
| return ret | |
| def gen_event_send(name: str, | |
| arg_type: Optional[QAPISchemaObjectType], | |
| features: List[QAPISchemaFeature], | |
| boxed: bool, | |
| event_enum_name: str, | |
| event_emit: str) -> str: | |
| # 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. | |
| have_args = boxed or (arg_type and not arg_type.is_empty()) | |
| ret = mcgen(''' | |
| %(proto)s | |
| { | |
| QDict *qmp; | |
| ''', | |
| proto=build_event_send_proto(name, arg_type, boxed)) | |
| if have_args: | |
| assert arg_type is not None | |
| ret += mcgen(''' | |
| QObject *obj; | |
| Visitor *v; | |
| ''') | |
| if not boxed: | |
| ret += gen_param_var(arg_type) | |
| for f in features: | |
| if f.is_special(): | |
| ret += mcgen(''' | |
| if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) { | |
| return; | |
| } | |
| ''', | |
| feat=f.name) | |
| ret += mcgen(''' | |
| qmp = qmp_event_build_dict("%(name)s"); | |
| ''', | |
| name=name) | |
| if have_args: | |
| assert arg_type is not None | |
| ret += mcgen(''' | |
| v = qobject_output_visitor_new_qmp(&obj); | |
| ''') | |
| if not arg_type.is_implicit(): | |
| ret += mcgen(''' | |
| visit_type_%(c_name)s(v, "%(name)s", &arg, &error_abort); | |
| ''', | |
| name=name, c_name=arg_type.c_name()) | |
| else: | |
| ret += mcgen(''' | |
| visit_start_struct(v, "%(name)s", NULL, 0, &error_abort); | |
| visit_type_%(c_name)s_members(v, ¶m, &error_abort); | |
| visit_check_struct(v, &error_abort); | |
| visit_end_struct(v, NULL); | |
| ''', | |
| name=name, c_name=arg_type.c_name()) | |
| ret += mcgen(''' | |
| visit_complete(v, &obj); | |
| if (qdict_size(qobject_to(QDict, obj))) { | |
| qdict_put_obj(qmp, "data", obj); | |
| } else { | |
| qobject_unref(obj); | |
| } | |
| ''') | |
| ret += mcgen(''' | |
| %(event_emit)s(%(c_enum)s, qmp); | |
| ''', | |
| event_emit=event_emit, | |
| c_enum=c_enum_const(event_enum_name, name)) | |
| if have_args: | |
| ret += mcgen(''' | |
| visit_free(v); | |
| ''') | |
| ret += mcgen(''' | |
| qobject_unref(qmp); | |
| } | |
| ''') | |
| return ret | |
| class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): | |
| def __init__(self, prefix: str): | |
| super().__init__( | |
| prefix, 'qapi-events', | |
| ' * Schema-defined QAPI/QMP events', None, __doc__) | |
| self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) | |
| self._event_enum_members: List[QAPISchemaEnumMember] = [] | |
| self._event_emit_name = c_name(prefix + 'qapi_event_emit') | |
| def _begin_user_module(self, name: str) -> None: | |
| events = self._module_basename('qapi-events', name) | |
| types = self._module_basename('qapi-types', name) | |
| visit = self._module_basename('qapi-visit', name) | |
| self._genc.add(mcgen(''' | |
| #include "qemu/osdep.h" | |
| #include "%(prefix)sqapi-emit-events.h" | |
| #include "%(events)s.h" | |
| #include "%(visit)s.h" | |
| #include "qapi/compat-policy.h" | |
| #include "qapi/error.h" | |
| #include "qobject/qdict.h" | |
| #include "qapi/qmp-event.h" | |
| ''', | |
| events=events, visit=visit, | |
| prefix=self._prefix)) | |
| self._genh.add(mcgen(''' | |
| #include "qapi/util.h" | |
| #include "%(types)s.h" | |
| ''', | |
| types=types)) | |
| def visit_end(self) -> None: | |
| self._add_module('./emit', ' * QAPI Events emission') | |
| self._genc.preamble_add(mcgen(''' | |
| #include "qemu/osdep.h" | |
| #include "%(prefix)sqapi-emit-events.h" | |
| ''', | |
| prefix=self._prefix)) | |
| self._genh.preamble_add(mcgen(''' | |
| #include "qapi/util.h" | |
| ''')) | |
| self._genh.add(gen_enum(self._event_enum_name, | |
| self._event_enum_members)) | |
| self._genc.add(gen_enum_lookup(self._event_enum_name, | |
| self._event_enum_members)) | |
| self._genh.add(mcgen(''' | |
| void %(event_emit)s(%(event_enum)s event, QDict *qdict); | |
| ''', | |
| event_emit=self._event_emit_name, | |
| event_enum=self._event_enum_name)) | |
| def visit_event(self, | |
| name: str, | |
| info: Optional[QAPISourceInfo], | |
| ifcond: QAPISchemaIfCond, | |
| features: List[QAPISchemaFeature], | |
| arg_type: Optional[QAPISchemaObjectType], | |
| boxed: bool) -> None: | |
| with ifcontext(ifcond, self._genh, self._genc): | |
| self._genh.add(gen_event_send_decl(name, arg_type, boxed)) | |
| self._genc.add(gen_event_send(name, arg_type, features, boxed, | |
| self._event_enum_name, | |
| self._event_emit_name)) | |
| # Note: we generate the enum member regardless of @ifcond, to | |
| # keep the enumeration usable in target-independent code. | |
| self._event_enum_members.append(QAPISchemaEnumMember(name, None)) | |
| def gen_events(schema: QAPISchema, | |
| output_dir: str, | |
| prefix: str) -> None: | |
| vis = QAPISchemaGenEventVisitor(prefix) | |
| schema.visit(vis) | |
| vis.write(output_dir) |