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_features, ifcontext 25from .schema import ( 26 QAPISchema, 27 QAPISchemaAlternatives, 28 QAPISchemaBranches, 29 QAPISchemaEnumMember, 30 QAPISchemaEnumType, 31 QAPISchemaFeature, 32 QAPISchemaIfCond, 33 QAPISchemaObjectType, 34 QAPISchemaObjectTypeMember, 35 QAPISchemaType, 36) 37from .source import QAPISourceInfo 38 39 40def gen_visit_decl(name: str, scalar: bool = False) -> str: 41 c_type = c_name(name) + ' *' 42 if not scalar: 43 c_type += '*' 44 return mcgen(''' 45 46bool visit_type_%(c_name)s(Visitor *v, const char *name, 47 %(c_type)sobj, Error **errp); 48''', 49 c_name=c_name(name), c_type=c_type) 50 51 52def gen_visit_members_decl(name: str) -> str: 53 return mcgen(''' 54 55bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); 56''', 57 c_name=c_name(name)) 58 59 60def gen_visit_object_members(name: str, 61 base: Optional[QAPISchemaObjectType], 62 members: List[QAPISchemaObjectTypeMember], 63 branches: Optional[QAPISchemaBranches]) -> str: 64 ret = mcgen(''' 65 66bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) 67{ 68''', 69 c_name=c_name(name)) 70 71 sep = '' 72 for memb in members: 73 if memb.optional and not memb.need_has(): 74 ret += memb.ifcond.gen_if() 75 ret += mcgen(''' 76 bool has_%(c_name)s = !!obj->%(c_name)s; 77''', 78 c_name=c_name(memb.name)) 79 sep = '\n' 80 ret += memb.ifcond.gen_endif() 81 ret += sep 82 83 if base: 84 ret += mcgen(''' 85 if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) { 86 return false; 87 } 88''', 89 c_type=base.c_name()) 90 91 for memb in members: 92 ret += memb.ifcond.gen_if() 93 if memb.optional: 94 has = 'has_' + c_name(memb.name) 95 if memb.need_has(): 96 has = 'obj->' + has 97 ret += mcgen(''' 98 if (visit_optional(v, "%(name)s", &%(has)s)) { 99''', 100 name=memb.name, has=has) 101 indent.increase() 102 features = gen_features(memb.features) 103 if features != '0': 104 ret += mcgen(''' 105 if (visit_policy_reject(v, "%(name)s", %(features)s, errp)) { 106 return false; 107 } 108 if (!visit_policy_skip(v, "%(name)s", %(features)s)) { 109''', 110 name=memb.name, features=features) 111 indent.increase() 112 ret += mcgen(''' 113 if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) { 114 return false; 115 } 116''', 117 c_type=memb.type.c_name(), name=memb.name, 118 c_name=c_name(memb.name)) 119 if features != '0': 120 indent.decrease() 121 ret += mcgen(''' 122 } 123''') 124 if memb.optional: 125 indent.decrease() 126 ret += mcgen(''' 127 } 128''') 129 ret += memb.ifcond.gen_endif() 130 131 if branches: 132 tag_member = branches.tag_member 133 assert isinstance(tag_member.type, QAPISchemaEnumType) 134 135 ret += mcgen(''' 136 switch (obj->%(c_name)s) { 137''', 138 c_name=c_name(tag_member.name)) 139 140 for var in branches.variants: 141 case_str = c_enum_const(tag_member.type.name, var.name, 142 tag_member.type.prefix) 143 ret += var.ifcond.gen_if() 144 if var.type.name == 'q_empty': 145 # valid variant and nothing to do 146 ret += mcgen(''' 147 case %(case)s: 148 break; 149''', 150 case=case_str) 151 else: 152 ret += mcgen(''' 153 case %(case)s: 154 return visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, errp); 155''', 156 case=case_str, 157 c_type=var.type.c_name(), c_name=c_name(var.name)) 158 159 ret += var.ifcond.gen_endif() 160 ret += mcgen(''' 161 default: 162 abort(); 163 } 164''') 165 166 ret += mcgen(''' 167 return true; 168} 169''') 170 return ret 171 172 173def gen_visit_list(name: str, element_type: QAPISchemaType) -> str: 174 return mcgen(''' 175 176bool visit_type_%(c_name)s(Visitor *v, const char *name, 177 %(c_name)s **obj, Error **errp) 178{ 179 bool ok = false; 180 %(c_name)s *tail; 181 size_t size = sizeof(**obj); 182 183 if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { 184 return false; 185 } 186 187 for (tail = *obj; tail; 188 tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) { 189 if (!visit_type_%(c_elt_type)s(v, NULL, &tail->value, errp)) { 190 goto out_obj; 191 } 192 } 193 194 ok = visit_check_list(v, errp); 195out_obj: 196 visit_end_list(v, (void **)obj); 197 if (!ok && visit_is_input(v)) { 198 qapi_free_%(c_name)s(*obj); 199 *obj = NULL; 200 } 201 return ok; 202} 203''', 204 c_name=c_name(name), c_elt_type=element_type.c_name()) 205 206 207def gen_visit_enum(name: str) -> str: 208 return mcgen(''' 209 210bool visit_type_%(c_name)s(Visitor *v, const char *name, 211 %(c_name)s *obj, Error **errp) 212{ 213 int value = *obj; 214 bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp); 215 *obj = value; 216 return ok; 217} 218''', 219 c_name=c_name(name)) 220 221 222def gen_visit_alternate(name: str, 223 alternatives: QAPISchemaAlternatives) -> 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 alternatives.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, 280 "Invalid parameter type for '%%s', expected: %(name)s", 281 name ? name : "null"); 282 /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */ 283 g_free(*obj); 284 *obj = NULL; 285 } 286out_obj: 287 visit_end_alternate(v, (void **)obj); 288 if (!ok && visit_is_input(v)) { 289 qapi_free_%(c_name)s(*obj); 290 *obj = NULL; 291 } 292 return ok; 293} 294''', 295 name=name, c_name=c_name(name)) 296 297 return ret 298 299 300def gen_visit_object(name: str) -> str: 301 return mcgen(''' 302 303bool visit_type_%(c_name)s(Visitor *v, const char *name, 304 %(c_name)s **obj, Error **errp) 305{ 306 bool ok = false; 307 308 if (!visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), errp)) { 309 return false; 310 } 311 if (!*obj) { 312 /* incomplete */ 313 assert(visit_is_dealloc(v)); 314 ok = true; 315 goto out_obj; 316 } 317 if (!visit_type_%(c_name)s_members(v, *obj, errp)) { 318 goto out_obj; 319 } 320 ok = visit_check_struct(v, errp); 321out_obj: 322 visit_end_struct(v, (void **)obj); 323 if (!ok && visit_is_input(v)) { 324 qapi_free_%(c_name)s(*obj); 325 *obj = NULL; 326 } 327 return ok; 328} 329''', 330 c_name=c_name(name)) 331 332 333class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): 334 335 def __init__(self, prefix: str): 336 super().__init__( 337 prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', 338 ' * Built-in QAPI visitors', __doc__) 339 340 def _begin_builtin_module(self) -> None: 341 self._genc.preamble_add(mcgen(''' 342#include "qemu/osdep.h" 343#include "qapi/error.h" 344#include "qapi/qapi-builtin-visit.h" 345''')) 346 self._genh.preamble_add(mcgen(''' 347#include "qapi/visitor.h" 348#include "qapi/qapi-builtin-types.h" 349 350''')) 351 352 def _begin_user_module(self, name: str) -> None: 353 types = self._module_basename('qapi-types', name) 354 visit = self._module_basename('qapi-visit', name) 355 self._genc.preamble_add(mcgen(''' 356#include "qemu/osdep.h" 357#include "qapi/error.h" 358#include "%(visit)s.h" 359#include "%(prefix)sqapi-features.h" 360''', 361 visit=visit, prefix=self._prefix)) 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 branches: Optional[QAPISchemaBranches]) -> 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, branches)) 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 alternatives: QAPISchemaAlternatives) -> 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, alternatives)) 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