]> Git Repo - qemu.git/blob - scripts/qapi/visit.py
qapi: Reject blank 'if' conditions in addition to empty ones
[qemu.git] / scripts / qapi / visit.py
1 """
2 QAPI visitor generator
3
4 Copyright IBM, Corp. 2011
5 Copyright (C) 2014-2018 Red Hat, Inc.
6
7 Authors:
8  Anthony Liguori <[email protected]>
9  Michael Roth    <[email protected]>
10  Markus Armbruster <[email protected]>
11
12 This work is licensed under the terms of the GNU GPL, version 2.
13 See the COPYING file in the top-level directory.
14 """
15
16 from qapi.common import *
17
18
19 def gen_visit_decl(name, scalar=False):
20     c_type = c_name(name) + ' *'
21     if not scalar:
22         c_type += '*'
23     return mcgen('''
24 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
25 ''',
26                  c_name=c_name(name), c_type=c_type)
27
28
29 def gen_visit_members_decl(name):
30     return mcgen('''
31
32 void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
33 ''',
34                  c_name=c_name(name))
35
36
37 def gen_visit_object_members(name, base, members, variants):
38     ret = mcgen('''
39
40 void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
41 {
42     Error *err = NULL;
43
44 ''',
45                 c_name=c_name(name))
46
47     if base:
48         ret += mcgen('''
49     visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
50     if (err) {
51         goto out;
52     }
53 ''',
54                      c_type=base.c_name())
55
56     for memb in members:
57         ret += gen_if(memb.ifcond)
58         if memb.optional:
59             ret += mcgen('''
60     if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
61 ''',
62                          name=memb.name, c_name=c_name(memb.name))
63             push_indent()
64         ret += mcgen('''
65     visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err);
66     if (err) {
67         goto out;
68     }
69 ''',
70                      c_type=memb.type.c_name(), name=memb.name,
71                      c_name=c_name(memb.name))
72         if memb.optional:
73             pop_indent()
74             ret += mcgen('''
75     }
76 ''')
77         ret += gen_endif(memb.ifcond)
78
79     if variants:
80         ret += mcgen('''
81     switch (obj->%(c_name)s) {
82 ''',
83                      c_name=c_name(variants.tag_member.name))
84
85         for var in variants.variants:
86             case_str = c_enum_const(variants.tag_member.type.name,
87                                     var.name,
88                                     variants.tag_member.type.prefix)
89             ret += gen_if(var.ifcond)
90             if var.type.name == 'q_empty':
91                 # valid variant and nothing to do
92                 ret += mcgen('''
93     case %(case)s:
94         break;
95 ''',
96                              case=case_str)
97             else:
98                 ret += mcgen('''
99     case %(case)s:
100         visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
101         break;
102 ''',
103                              case=case_str,
104                              c_type=var.type.c_name(), c_name=c_name(var.name))
105
106             ret += gen_endif(var.ifcond)
107         ret += mcgen('''
108     default:
109         abort();
110     }
111 ''')
112
113     # 'goto out' produced for base, for each member, and if variants were
114     # present
115     if base or members or variants:
116         ret += mcgen('''
117
118 out:
119 ''')
120     ret += mcgen('''
121     error_propagate(errp, err);
122 }
123 ''')
124     return ret
125
126
127 def gen_visit_list(name, element_type):
128     return mcgen('''
129
130 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
131 {
132     Error *err = NULL;
133     %(c_name)s *tail;
134     size_t size = sizeof(**obj);
135
136     visit_start_list(v, name, (GenericList **)obj, size, &err);
137     if (err) {
138         goto out;
139     }
140
141     for (tail = *obj; tail;
142          tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) {
143         visit_type_%(c_elt_type)s(v, NULL, &tail->value, &err);
144         if (err) {
145             break;
146         }
147     }
148
149     if (!err) {
150         visit_check_list(v, &err);
151     }
152     visit_end_list(v, (void **)obj);
153     if (err && visit_is_input(v)) {
154         qapi_free_%(c_name)s(*obj);
155         *obj = NULL;
156     }
157 out:
158     error_propagate(errp, err);
159 }
160 ''',
161                  c_name=c_name(name), c_elt_type=element_type.c_name())
162
163
164 def gen_visit_enum(name):
165     return mcgen('''
166
167 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
168 {
169     int value = *obj;
170     visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
171     *obj = value;
172 }
173 ''',
174                  c_name=c_name(name))
175
176
177 def gen_visit_alternate(name, variants):
178     ret = mcgen('''
179
180 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
181 {
182     Error *err = NULL;
183
184     visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj),
185                           &err);
186     if (err) {
187         goto out;
188     }
189     if (!*obj) {
190         goto out_obj;
191     }
192     switch ((*obj)->type) {
193 ''',
194                 c_name=c_name(name))
195
196     for var in variants.variants:
197         ret += gen_if(var.ifcond)
198         ret += mcgen('''
199     case %(case)s:
200 ''',
201                      case=var.type.alternate_qtype())
202         if isinstance(var.type, QAPISchemaObjectType):
203             ret += mcgen('''
204         visit_start_struct(v, name, NULL, 0, &err);
205         if (err) {
206             break;
207         }
208         visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err);
209         if (!err) {
210             visit_check_struct(v, &err);
211         }
212         visit_end_struct(v, NULL);
213 ''',
214                          c_type=var.type.c_name(),
215                          c_name=c_name(var.name))
216         else:
217             ret += mcgen('''
218         visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
219 ''',
220                          c_type=var.type.c_name(),
221                          c_name=c_name(var.name))
222         ret += mcgen('''
223         break;
224 ''')
225         ret += gen_endif(var.ifcond)
226
227     ret += mcgen('''
228     case QTYPE_NONE:
229         abort();
230     default:
231         error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
232                    "%(name)s");
233     }
234 out_obj:
235     visit_end_alternate(v, (void **)obj);
236     if (err && visit_is_input(v)) {
237         qapi_free_%(c_name)s(*obj);
238         *obj = NULL;
239     }
240 out:
241     error_propagate(errp, err);
242 }
243 ''',
244                  name=name, c_name=c_name(name))
245
246     return ret
247
248
249 def gen_visit_object(name, base, members, variants):
250     return mcgen('''
251
252 void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
253 {
254     Error *err = NULL;
255
256     visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
257     if (err) {
258         goto out;
259     }
260     if (!*obj) {
261         goto out_obj;
262     }
263     visit_type_%(c_name)s_members(v, *obj, &err);
264     if (err) {
265         goto out_obj;
266     }
267     visit_check_struct(v, &err);
268 out_obj:
269     visit_end_struct(v, (void **)obj);
270     if (err && visit_is_input(v)) {
271         qapi_free_%(c_name)s(*obj);
272         *obj = NULL;
273     }
274 out:
275     error_propagate(errp, err);
276 }
277 ''',
278                  c_name=c_name(name))
279
280
281 class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
282
283     def __init__(self, prefix):
284         QAPISchemaModularCVisitor.__init__(
285             self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
286             __doc__)
287         self._add_system_module(None, ' * Built-in QAPI visitors')
288         self._genc.preamble_add(mcgen('''
289 #include "qemu/osdep.h"
290 #include "qapi/error.h"
291 #include "qapi/qapi-builtin-visit.h"
292 '''))
293         self._genh.preamble_add(mcgen('''
294 #include "qapi/visitor.h"
295 #include "qapi/qapi-builtin-types.h"
296
297 ''',
298                                       prefix=prefix))
299
300     def _begin_user_module(self, name):
301         types = self._module_basename('qapi-types', name)
302         visit = self._module_basename('qapi-visit', name)
303         self._genc.preamble_add(mcgen('''
304 #include "qemu/osdep.h"
305 #include "qapi/error.h"
306 #include "qapi/qmp/qerror.h"
307 #include "%(visit)s.h"
308 ''',
309                                       visit=visit))
310         self._genh.preamble_add(mcgen('''
311 #include "qapi/qapi-builtin-visit.h"
312 #include "%(types)s.h"
313
314 ''',
315                                       types=types))
316
317     def visit_enum_type(self, name, info, ifcond, members, prefix):
318         with ifcontext(ifcond, self._genh, self._genc):
319             self._genh.add(gen_visit_decl(name, scalar=True))
320             self._genc.add(gen_visit_enum(name))
321
322     def visit_array_type(self, name, info, ifcond, element_type):
323         with ifcontext(ifcond, self._genh, self._genc):
324             self._genh.add(gen_visit_decl(name))
325             self._genc.add(gen_visit_list(name, element_type))
326
327     def visit_object_type(self, name, info, ifcond, base, members, variants,
328                           features):
329         # Nothing to do for the special empty builtin
330         if name == 'q_empty':
331             return
332         with ifcontext(ifcond, self._genh, self._genc):
333             self._genh.add(gen_visit_members_decl(name))
334             self._genc.add(gen_visit_object_members(name, base,
335                                                     members, variants))
336             # TODO Worth changing the visitor signature, so we could
337             # directly use rather than repeat type.is_implicit()?
338             if not name.startswith('q_'):
339                 # only explicit types need an allocating visit
340                 self._genh.add(gen_visit_decl(name))
341                 self._genc.add(gen_visit_object(name, base, members, variants))
342
343     def visit_alternate_type(self, name, info, ifcond, variants):
344         with ifcontext(ifcond, self._genh, self._genc):
345             self._genh.add(gen_visit_decl(name))
346             self._genc.add(gen_visit_alternate(name, variants))
347
348
349 def gen_visit(schema, output_dir, prefix, opt_builtins):
350     vis = QAPISchemaGenVisitVisitor(prefix)
351     schema.visit(vis)
352     vis.write(output_dir, opt_builtins)
This page took 0.042439 seconds and 4 git commands to generate.