| 1 | """ |
| 2 | QAPI event generator |
| 3 | |
| 4 | Copyright (c) 2014 Wenchao Xia |
| 5 | Copyright (c) 2015-2018 Red Hat Inc. |
| 6 | |
| 7 | Authors: |
| 8 | Wenchao Xia <wenchaoqemu@gmail.com> |
| 9 | Markus Armbruster <armbru@redhat.com> |
| 10 | |
| 11 | This work is licensed under the terms of the GNU GPL, version 2. |
| 12 | See the COPYING file in the top-level directory. |
| 13 | """ |
| 14 | |
| 15 | from typing import List, Optional |
| 16 | |
| 17 | from .common import c_enum_const, c_name, mcgen |
| 18 | from .gen import QAPISchemaModularCVisitor, build_params, ifcontext |
| 19 | from .schema import ( |
| 20 | QAPISchema, |
| 21 | QAPISchemaEnumMember, |
| 22 | QAPISchemaFeature, |
| 23 | QAPISchemaIfCond, |
| 24 | QAPISchemaObjectType, |
| 25 | ) |
| 26 | from .source import QAPISourceInfo |
| 27 | from .types import gen_enum, gen_enum_lookup |
| 28 | |
| 29 | |
| 30 | def build_event_send_proto(name: str, |
| 31 | arg_type: Optional[QAPISchemaObjectType], |
| 32 | boxed: bool) -> str: |
| 33 | return 'void qapi_event_send_%(c_name)s(%(param)s)' % { |
| 34 | 'c_name': c_name(name.lower()), |
| 35 | 'param': build_params(arg_type, boxed)} |
| 36 | |
| 37 | |
| 38 | def gen_event_send_decl(name: str, |
| 39 | arg_type: Optional[QAPISchemaObjectType], |
| 40 | boxed: bool) -> str: |
| 41 | return mcgen(''' |
| 42 | |
| 43 | %(proto)s; |
| 44 | ''', |
| 45 | proto=build_event_send_proto(name, arg_type, boxed)) |
| 46 | |
| 47 | |
| 48 | def gen_param_var(typ: QAPISchemaObjectType) -> str: |
| 49 | """ |
| 50 | Generate a struct variable holding the event parameters. |
| 51 | |
| 52 | Initialize it with the function arguments defined in `gen_event_send`. |
| 53 | """ |
| 54 | assert not typ.branches |
| 55 | ret = mcgen(''' |
| 56 | %(c_name)s param = { |
| 57 | ''', |
| 58 | c_name=typ.c_name()) |
| 59 | sep = ' ' |
| 60 | for memb in typ.members: |
| 61 | ret += sep |
| 62 | sep = ', ' |
| 63 | if memb.need_has(): |
| 64 | ret += 'has_' + c_name(memb.name) + sep |
| 65 | if memb.type.name == 'str': |
| 66 | # Cast away const added in build_params() |
| 67 | ret += '(char *)' |
| 68 | ret += c_name(memb.name) |
| 69 | ret += mcgen(''' |
| 70 | |
| 71 | }; |
| 72 | ''') |
| 73 | if not typ.is_implicit(): |
| 74 | ret += mcgen(''' |
| 75 | %(c_name)s *arg = ¶m; |
| 76 | ''', |
| 77 | c_name=typ.c_name()) |
| 78 | return ret |
| 79 | |
| 80 | |
| 81 | def gen_event_send(name: str, |
| 82 | arg_type: Optional[QAPISchemaObjectType], |
| 83 | features: List[QAPISchemaFeature], |
| 84 | boxed: bool, |
| 85 | event_enum_name: str, |
| 86 | event_emit: str) -> str: |
| 87 | # FIXME: Our declaration of local variables (and of 'errp' in the |
| 88 | # parameter list) can collide with exploded members of the event's |
| 89 | # data type passed in as parameters. If this collision ever hits in |
| 90 | # practice, we can rename our local variables with a leading _ prefix, |
| 91 | # or split the code into a wrapper function that creates a boxed |
| 92 | # 'param' object then calls another to do the real work. |
| 93 | have_args = boxed or (arg_type and not arg_type.is_empty()) |
| 94 | |
| 95 | ret = mcgen(''' |
| 96 | |
| 97 | %(proto)s |
| 98 | { |
| 99 | QDict *qmp; |
| 100 | ''', |
| 101 | proto=build_event_send_proto(name, arg_type, boxed)) |
| 102 | |
| 103 | if have_args: |
| 104 | assert arg_type is not None |
| 105 | ret += mcgen(''' |
| 106 | QObject *obj; |
| 107 | Visitor *v; |
| 108 | ''') |
| 109 | if not boxed: |
| 110 | ret += gen_param_var(arg_type) |
| 111 | |
| 112 | for f in features: |
| 113 | if f.is_special(): |
| 114 | ret += mcgen(''' |
| 115 | |
| 116 | if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) { |
| 117 | return; |
| 118 | } |
| 119 | ''', |
| 120 | feat=f.name) |
| 121 | |
| 122 | ret += mcgen(''' |
| 123 | |
| 124 | qmp = qmp_event_build_dict("%(name)s"); |
| 125 | |
| 126 | ''', |
| 127 | name=name) |
| 128 | |
| 129 | if have_args: |
| 130 | assert arg_type is not None |
| 131 | ret += mcgen(''' |
| 132 | v = qobject_output_visitor_new_qmp(&obj); |
| 133 | ''') |
| 134 | if not arg_type.is_implicit(): |
| 135 | ret += mcgen(''' |
| 136 | visit_type_%(c_name)s(v, "%(name)s", &arg, &error_abort); |
| 137 | ''', |
| 138 | name=name, c_name=arg_type.c_name()) |
| 139 | else: |
| 140 | ret += mcgen(''' |
| 141 | |
| 142 | visit_start_struct(v, "%(name)s", NULL, 0, &error_abort); |
| 143 | visit_type_%(c_name)s_members(v, ¶m, &error_abort); |
| 144 | visit_check_struct(v, &error_abort); |
| 145 | visit_end_struct(v, NULL); |
| 146 | ''', |
| 147 | name=name, c_name=arg_type.c_name()) |
| 148 | ret += mcgen(''' |
| 149 | |
| 150 | visit_complete(v, &obj); |
| 151 | if (qdict_size(qobject_to(QDict, obj))) { |
| 152 | qdict_put_obj(qmp, "data", obj); |
| 153 | } else { |
| 154 | qobject_unref(obj); |
| 155 | } |
| 156 | ''') |
| 157 | |
| 158 | ret += mcgen(''' |
| 159 | %(event_emit)s(%(c_enum)s, qmp); |
| 160 | |
| 161 | ''', |
| 162 | event_emit=event_emit, |
| 163 | c_enum=c_enum_const(event_enum_name, name)) |
| 164 | |
| 165 | if have_args: |
| 166 | ret += mcgen(''' |
| 167 | visit_free(v); |
| 168 | ''') |
| 169 | ret += mcgen(''' |
| 170 | qobject_unref(qmp); |
| 171 | } |
| 172 | ''') |
| 173 | return ret |
| 174 | |
| 175 | |
| 176 | class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): |
| 177 | |
| 178 | def __init__(self, prefix: str): |
| 179 | super().__init__( |
| 180 | prefix, 'qapi-events', |
| 181 | ' * Schema-defined QAPI/QMP events', None, __doc__) |
| 182 | self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) |
| 183 | self._event_enum_members: List[QAPISchemaEnumMember] = [] |
| 184 | self._event_emit_name = c_name(prefix + 'qapi_event_emit') |
| 185 | |
| 186 | def _begin_user_module(self, name: str) -> None: |
| 187 | events = self._module_basename('qapi-events', name) |
| 188 | types = self._module_basename('qapi-types', name) |
| 189 | visit = self._module_basename('qapi-visit', name) |
| 190 | self._genc.add(mcgen(''' |
| 191 | #include "qemu/osdep.h" |
| 192 | #include "%(prefix)sqapi-emit-events.h" |
| 193 | #include "%(events)s.h" |
| 194 | #include "%(visit)s.h" |
| 195 | #include "qapi/compat-policy.h" |
| 196 | #include "qapi/error.h" |
| 197 | #include "qobject/qdict.h" |
| 198 | #include "qapi/qmp-event.h" |
| 199 | ''', |
| 200 | events=events, visit=visit, |
| 201 | prefix=self._prefix)) |
| 202 | self._genh.add(mcgen(''' |
| 203 | #include "qapi/util.h" |
| 204 | #include "%(types)s.h" |
| 205 | ''', |
| 206 | types=types)) |
| 207 | |
| 208 | def visit_end(self) -> None: |
| 209 | self._add_module('./emit', ' * QAPI Events emission') |
| 210 | self._genc.preamble_add(mcgen(''' |
| 211 | #include "qemu/osdep.h" |
| 212 | #include "%(prefix)sqapi-emit-events.h" |
| 213 | ''', |
| 214 | prefix=self._prefix)) |
| 215 | self._genh.preamble_add(mcgen(''' |
| 216 | #include "qapi/util.h" |
| 217 | ''')) |
| 218 | self._genh.add(gen_enum(self._event_enum_name, |
| 219 | self._event_enum_members)) |
| 220 | self._genc.add(gen_enum_lookup(self._event_enum_name, |
| 221 | self._event_enum_members)) |
| 222 | self._genh.add(mcgen(''' |
| 223 | |
| 224 | void %(event_emit)s(%(event_enum)s event, QDict *qdict); |
| 225 | ''', |
| 226 | event_emit=self._event_emit_name, |
| 227 | event_enum=self._event_enum_name)) |
| 228 | |
| 229 | def visit_event(self, |
| 230 | name: str, |
| 231 | info: Optional[QAPISourceInfo], |
| 232 | ifcond: QAPISchemaIfCond, |
| 233 | features: List[QAPISchemaFeature], |
| 234 | arg_type: Optional[QAPISchemaObjectType], |
| 235 | boxed: bool) -> None: |
| 236 | with ifcontext(ifcond, self._genh, self._genc): |
| 237 | self._genh.add(gen_event_send_decl(name, arg_type, boxed)) |
| 238 | self._genc.add(gen_event_send(name, arg_type, features, boxed, |
| 239 | self._event_enum_name, |
| 240 | self._event_emit_name)) |
| 241 | # Note: we generate the enum member regardless of @ifcond, to |
| 242 | # keep the enumeration usable in target-independent code. |
| 243 | self._event_enum_members.append(QAPISchemaEnumMember(name, None)) |
| 244 | |
| 245 | |
| 246 | def gen_events(schema: QAPISchema, |
| 247 | output_dir: str, |
| 248 | prefix: str) -> None: |
| 249 | vis = QAPISchemaGenEventVisitor(prefix) |
| 250 | schema.visit(vis) |
| 251 | vis.write(output_dir) |