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