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