]>
Commit | Line | Data |
---|---|---|
5ddeec83 MA |
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 | """ | |
21cd70df | 14 | |
f17539c8 | 15 | from typing import List, Optional |
d1b21b39 | 16 | |
e6a34cd7 JS |
17 | from .common import c_enum_const, c_name, mcgen |
18 | from .gen import QAPISchemaModularCVisitor, build_params, ifcontext | |
d1b21b39 JS |
19 | from .schema import ( |
20 | QAPISchema, | |
21 | QAPISchemaEnumMember, | |
22 | QAPISchemaFeature, | |
f17539c8 | 23 | QAPISchemaIfCond, |
d1b21b39 JS |
24 | QAPISchemaObjectType, |
25 | ) | |
26 | from .source import QAPISourceInfo | |
7137a960 | 27 | from .types import gen_enum, gen_enum_lookup |
21cd70df | 28 | |
e98859a9 | 29 | |
d1b21b39 | 30 | def build_event_send_proto(name: str, |
3cc01c54 | 31 | arg_type: Optional[QAPISchemaObjectType], |
d1b21b39 | 32 | boxed: bool) -> str: |
03b4367a MA |
33 | return 'void qapi_event_send_%(c_name)s(%(param)s)' % { |
34 | 'c_name': c_name(name.lower()), | |
3ab72385 | 35 | 'param': build_params(arg_type, boxed)} |
21cd70df | 36 | |
21cd70df | 37 | |
d1b21b39 | 38 | def gen_event_send_decl(name: str, |
3cc01c54 | 39 | arg_type: Optional[QAPISchemaObjectType], |
d1b21b39 | 40 | boxed: bool) -> str: |
21cd70df WX |
41 | return mcgen(''' |
42 | ||
e98859a9 | 43 | %(proto)s; |
21cd70df | 44 | ''', |
086ee7a6 | 45 | proto=build_event_send_proto(name, arg_type, boxed)) |
e98859a9 | 46 | |
21cd70df | 47 | |
d1b21b39 | 48 | def gen_param_var(typ: QAPISchemaObjectType) -> str: |
1a503761 JS |
49 | """ |
50 | Generate a struct variable holding the event parameters. | |
51 | ||
52 | Initialize it with the function arguments defined in `gen_event_send`. | |
53 | """ | |
0949e95b EB |
54 | assert not typ.variants |
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 = ', ' | |
44ea9d9b | 63 | if memb.need_has(): |
0949e95b EB |
64 | ret += 'has_' + c_name(memb.name) + sep |
65 | if memb.type.name == 'str': | |
086ee7a6 | 66 | # Cast away const added in build_params() |
0949e95b EB |
67 | ret += '(char *)' |
68 | ret += c_name(memb.name) | |
69 | ret += mcgen(''' | |
70 | ||
71 | }; | |
72 | ''') | |
4d0b268f EB |
73 | if not typ.is_implicit(): |
74 | ret += mcgen(''' | |
75 | %(c_name)s *arg = ¶m; | |
76 | ''', | |
77 | c_name=typ.c_name()) | |
0949e95b EB |
78 | return ret |
79 | ||
80 | ||
d1b21b39 | 81 | def gen_event_send(name: str, |
3cc01c54 | 82 | arg_type: Optional[QAPISchemaObjectType], |
278fc2f7 | 83 | features: List[QAPISchemaFeature], |
d1b21b39 JS |
84 | boxed: bool, |
85 | event_enum_name: str, | |
86 | event_emit: str) -> str: | |
0949e95b EB |
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. | |
675b214b MA |
93 | have_args = boxed or (arg_type and not arg_type.is_empty()) |
94 | ||
e98859a9 | 95 | ret = mcgen(''' |
21cd70df | 96 | |
e98859a9 | 97 | %(proto)s |
21cd70df WX |
98 | { |
99 | QDict *qmp; | |
e98859a9 | 100 | ''', |
086ee7a6 | 101 | proto=build_event_send_proto(name, arg_type, boxed)) |
21cd70df | 102 | |
675b214b | 103 | if have_args: |
3cc01c54 | 104 | assert arg_type is not None |
e98859a9 | 105 | ret += mcgen(''' |
3b098d56 | 106 | QObject *obj; |
21cd70df | 107 | Visitor *v; |
e98859a9 | 108 | ''') |
c818408e EB |
109 | if not boxed: |
110 | ret += gen_param_var(arg_type) | |
21cd70df | 111 | |
57df0dff MA |
112 | for f in features: |
113 | if f.is_special(): | |
114 | ret += mcgen(''' | |
278fc2f7 | 115 | |
57df0dff | 116 | if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) { |
278fc2f7 MA |
117 | return; |
118 | } | |
57df0dff MA |
119 | ''', |
120 | feat=f.name) | |
278fc2f7 | 121 | |
e98859a9 | 122 | ret += mcgen(''' |
0949e95b | 123 | |
e98859a9 | 124 | qmp = qmp_event_build_dict("%(name)s"); |
21cd70df | 125 | |
e98859a9 MA |
126 | ''', |
127 | name=name) | |
21cd70df | 128 | |
675b214b | 129 | if have_args: |
3cc01c54 | 130 | assert arg_type is not None |
e98859a9 | 131 | ret += mcgen(''' |
a291a38f | 132 | v = qobject_output_visitor_new_qmp(&obj); |
4d0b268f EB |
133 | ''') |
134 | if not arg_type.is_implicit(): | |
135 | ret += mcgen(''' | |
3ab72385 | 136 | visit_type_%(c_name)s(v, "%(name)s", &arg, &error_abort); |
4d0b268f EB |
137 | ''', |
138 | name=name, c_name=arg_type.c_name()) | |
139 | else: | |
140 | ret += mcgen(''' | |
21cd70df | 141 | |
3ab72385 PX |
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); | |
1158bb2a | 145 | visit_end_struct(v, NULL); |
4d0b268f EB |
146 | ''', |
147 | name=name, c_name=arg_type.c_name()) | |
148 | ret += mcgen(''' | |
21cd70df | 149 | |
3b098d56 | 150 | visit_complete(v, &obj); |
a291a38f MA |
151 | if (qdict_size(qobject_to(QDict, obj))) { |
152 | qdict_put_obj(qmp, "data", obj); | |
153 | } else { | |
154 | qobject_unref(obj); | |
155 | } | |
4d0b268f | 156 | ''') |
21cd70df | 157 | |
e98859a9 | 158 | ret += mcgen(''' |
a9529100 | 159 | %(event_emit)s(%(c_enum)s, qmp); |
21cd70df | 160 | |
e98859a9 | 161 | ''', |
a9529100 | 162 | event_emit=event_emit, |
e98859a9 | 163 | c_enum=c_enum_const(event_enum_name, name)) |
21cd70df | 164 | |
675b214b | 165 | if have_args: |
e98859a9 | 166 | ret += mcgen(''' |
2c0ef9f4 | 167 | visit_free(v); |
e98859a9 MA |
168 | ''') |
169 | ret += mcgen(''' | |
cb3e7f08 | 170 | qobject_unref(qmp); |
21cd70df | 171 | } |
e98859a9 | 172 | ''') |
21cd70df WX |
173 | return ret |
174 | ||
05f43a96 | 175 | |
252dc310 | 176 | class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): |
71b3f045 | 177 | |
d1b21b39 | 178 | def __init__(self, prefix: str): |
2cae67bc MA |
179 | super().__init__( |
180 | prefix, 'qapi-events', | |
3bef3aae | 181 | ' * Schema-defined QAPI/QMP events', None, __doc__) |
1962bd39 | 182 | self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) |
d1b21b39 | 183 | self._event_enum_members: List[QAPISchemaEnumMember] = [] |
a9529100 | 184 | self._event_emit_name = c_name(prefix + 'qapi_event_emit') |
252dc310 | 185 | |
d1b21b39 | 186 | def _begin_user_module(self, name: str) -> None: |
5d75648b | 187 | events = self._module_basename('qapi-events', name) |
9af23989 MA |
188 | types = self._module_basename('qapi-types', name) |
189 | visit = self._module_basename('qapi-visit', name) | |
71b3f045 | 190 | self._genc.add(mcgen(''' |
9167ebd9 | 191 | #include "qemu/osdep.h" |
5d75648b MA |
192 | #include "%(prefix)sqapi-emit-events.h" |
193 | #include "%(events)s.h" | |
9af23989 | 194 | #include "%(visit)s.h" |
278fc2f7 | 195 | #include "qapi/compat-policy.h" |
e688df6b | 196 | #include "qapi/error.h" |
452fcdbc | 197 | #include "qapi/qmp/qdict.h" |
12f8e1b9 | 198 | #include "qapi/qmp-event.h" |
12f8e1b9 | 199 | ''', |
5d75648b MA |
200 | events=events, visit=visit, |
201 | prefix=self._prefix)) | |
71b3f045 | 202 | self._genh.add(mcgen(''' |
5b5f825d | 203 | #include "qapi/util.h" |
9af23989 | 204 | #include "%(types)s.h" |
21cd70df | 205 | ''', |
9af23989 | 206 | types=types)) |
26df4e7f | 207 | |
d1b21b39 | 208 | def visit_end(self) -> None: |
4ab0ff6d | 209 | self._add_module('./emit', ' * QAPI Events emission') |
5d75648b MA |
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(''' | |
a9529100 MA |
223 | |
224 | void %(event_emit)s(%(event_enum)s event, QDict *qdict); | |
225 | ''', | |
5d75648b MA |
226 | event_emit=self._event_emit_name, |
227 | event_enum=self._event_enum_name)) | |
71b3f045 | 228 | |
d1b21b39 JS |
229 | def visit_event(self, |
230 | name: str, | |
4a82e468 | 231 | info: Optional[QAPISourceInfo], |
f17539c8 | 232 | ifcond: QAPISchemaIfCond, |
d1b21b39 | 233 | features: List[QAPISchemaFeature], |
3cc01c54 | 234 | arg_type: Optional[QAPISchemaObjectType], |
d1b21b39 | 235 | boxed: bool) -> None: |
c3cd6aa0 MAL |
236 | with ifcontext(ifcond, self._genh, self._genc): |
237 | self._genh.add(gen_event_send_decl(name, arg_type, boxed)) | |
278fc2f7 | 238 | self._genc.add(gen_event_send(name, arg_type, features, boxed, |
a9529100 MA |
239 | self._event_enum_name, |
240 | self._event_emit_name)) | |
093e3679 MA |
241 | # Note: we generate the enum member regardless of @ifcond, to |
242 | # keep the enumeration usable in target-independent code. | |
e6f9678d | 243 | self._event_enum_members.append(QAPISchemaEnumMember(name, None)) |
71b3f045 MA |
244 | |
245 | ||
d1b21b39 JS |
246 | def gen_events(schema: QAPISchema, |
247 | output_dir: str, | |
248 | prefix: str) -> None: | |
26df4e7f MA |
249 | vis = QAPISchemaGenEventVisitor(prefix) |
250 | schema.visit(vis) | |
71b3f045 | 251 | vis.write(output_dir) |