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