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