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