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