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