1# 2# QAPI visitor generator 3# 4# Copyright IBM, Corp. 2011 5# Copyright (C) 2014-2016 Red Hat, Inc. 6# 7# Authors: 8# Anthony Liguori <aliguori@us.ibm.com> 9# Michael Roth <mdroth@linux.vnet.ibm.com> 10# Markus Armbruster <armbru@redhat.com> 11# 12# This work is licensed under the terms of the GNU GPL, version 2. 13# See the COPYING file in the top-level directory. 14 15from qapi import * 16 17 18def gen_visit_decl(name, scalar=False): 19 c_type = c_name(name) + ' *' 20 if not scalar: 21 c_type += '*' 22 return mcgen(''' 23void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp); 24''', 25 c_name=c_name(name), c_type=c_type) 26 27 28def gen_visit_members_decl(name): 29 return mcgen(''' 30 31void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); 32''', 33 c_name=c_name(name)) 34 35 36def gen_visit_object_members(name, base, members, variants): 37 ret = mcgen(''' 38 39void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) 40{ 41 Error *err = NULL; 42 43''', 44 c_name=c_name(name)) 45 46 if base: 47 ret += mcgen(''' 48 visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err); 49 if (err) { 50 goto out; 51 } 52''', 53 c_type=base.c_name()) 54 55 for memb in members: 56 if memb.optional: 57 ret += mcgen(''' 58 if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { 59''', 60 name=memb.name, c_name=c_name(memb.name)) 61 push_indent() 62 ret += mcgen(''' 63 visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err); 64 if (err) { 65 goto out; 66 } 67''', 68 c_type=memb.type.c_name(), name=memb.name, 69 c_name=c_name(memb.name)) 70 if memb.optional: 71 pop_indent() 72 ret += mcgen(''' 73 } 74''') 75 76 if variants: 77 ret += mcgen(''' 78 switch (obj->%(c_name)s) { 79''', 80 c_name=c_name(variants.tag_member.name)) 81 82 for var in variants.variants: 83 ret += mcgen(''' 84 case %(case)s: 85 visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err); 86 break; 87''', 88 case=c_enum_const(variants.tag_member.type.name, 89 var.name, 90 variants.tag_member.type.prefix), 91 c_type=var.type.c_name(), c_name=c_name(var.name)) 92 93 ret += mcgen(''' 94 default: 95 abort(); 96 } 97''') 98 99 # 'goto out' produced for base, for each member, and if variants were 100 # present 101 if base or members or variants: 102 ret += mcgen(''' 103 104out: 105''') 106 ret += mcgen(''' 107 error_propagate(errp, err); 108} 109''') 110 return ret 111 112 113def gen_visit_list(name, element_type): 114 return mcgen(''' 115 116void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 117{ 118 Error *err = NULL; 119 %(c_name)s *tail; 120 size_t size = sizeof(**obj); 121 122 visit_start_list(v, name, (GenericList **)obj, size, &err); 123 if (err) { 124 goto out; 125 } 126 127 for (tail = *obj; tail; 128 tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) { 129 visit_type_%(c_elt_type)s(v, NULL, &tail->value, &err); 130 if (err) { 131 break; 132 } 133 } 134 135 if (!err) { 136 visit_check_list(v, &err); 137 } 138 visit_end_list(v, (void **)obj); 139 if (err && visit_is_input(v)) { 140 qapi_free_%(c_name)s(*obj); 141 *obj = NULL; 142 } 143out: 144 error_propagate(errp, err); 145} 146''', 147 c_name=c_name(name), c_elt_type=element_type.c_name()) 148 149 150def gen_visit_enum(name): 151 return mcgen(''' 152 153void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp) 154{ 155 int value = *obj; 156 visit_type_enum(v, name, &value, %(c_name)s_lookup, errp); 157 *obj = value; 158} 159''', 160 c_name=c_name(name)) 161 162 163def gen_visit_alternate(name, variants): 164 promote_int = 'true' 165 ret = '' 166 for var in variants.variants: 167 if var.type.alternate_qtype() == 'QTYPE_QINT': 168 promote_int = 'false' 169 170 ret += mcgen(''' 171 172void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 173{ 174 Error *err = NULL; 175 176 visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj), 177 %(promote_int)s, &err); 178 if (err) { 179 goto out; 180 } 181 if (!*obj) { 182 goto out_obj; 183 } 184 switch ((*obj)->type) { 185''', 186 c_name=c_name(name), promote_int=promote_int) 187 188 for var in variants.variants: 189 ret += mcgen(''' 190 case %(case)s: 191''', 192 case=var.type.alternate_qtype()) 193 if isinstance(var.type, QAPISchemaObjectType): 194 ret += mcgen(''' 195 visit_start_struct(v, name, NULL, 0, &err); 196 if (err) { 197 break; 198 } 199 visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err); 200 if (!err) { 201 visit_check_struct(v, &err); 202 } 203 visit_end_struct(v, NULL); 204''', 205 c_type=var.type.c_name(), 206 c_name=c_name(var.name)) 207 else: 208 ret += mcgen(''' 209 visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err); 210''', 211 c_type=var.type.c_name(), 212 c_name=c_name(var.name)) 213 ret += mcgen(''' 214 break; 215''') 216 217 ret += mcgen(''' 218 case QTYPE_NONE: 219 abort(); 220 default: 221 error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 222 "%(name)s"); 223 } 224out_obj: 225 visit_end_alternate(v, (void **)obj); 226 if (err && visit_is_input(v)) { 227 qapi_free_%(c_name)s(*obj); 228 *obj = NULL; 229 } 230out: 231 error_propagate(errp, err); 232} 233''', 234 name=name, c_name=c_name(name)) 235 236 return ret 237 238 239def gen_visit_object(name, base, members, variants): 240 return mcgen(''' 241 242void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 243{ 244 Error *err = NULL; 245 246 visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err); 247 if (err) { 248 goto out; 249 } 250 if (!*obj) { 251 goto out_obj; 252 } 253 visit_type_%(c_name)s_members(v, *obj, &err); 254 if (err) { 255 goto out_obj; 256 } 257 visit_check_struct(v, &err); 258out_obj: 259 visit_end_struct(v, (void **)obj); 260 if (err && visit_is_input(v)) { 261 qapi_free_%(c_name)s(*obj); 262 *obj = NULL; 263 } 264out: 265 error_propagate(errp, err); 266} 267''', 268 c_name=c_name(name)) 269 270 271class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): 272 def __init__(self): 273 self.decl = None 274 self.defn = None 275 self._btin = None 276 277 def visit_begin(self, schema): 278 self.decl = '' 279 self.defn = '' 280 self._btin = guardstart('QAPI_VISIT_BUILTIN') 281 282 def visit_end(self): 283 # To avoid header dependency hell, we always generate 284 # declarations for built-in types in our header files and 285 # simply guard them. See also do_builtins (command line 286 # option -b). 287 self._btin += guardend('QAPI_VISIT_BUILTIN') 288 self.decl = self._btin + self.decl 289 self._btin = None 290 291 def visit_enum_type(self, name, info, values, prefix): 292 # Special case for our lone builtin enum type 293 # TODO use something cleaner than existence of info 294 if not info: 295 self._btin += gen_visit_decl(name, scalar=True) 296 if do_builtins: 297 self.defn += gen_visit_enum(name) 298 else: 299 self.decl += gen_visit_decl(name, scalar=True) 300 self.defn += gen_visit_enum(name) 301 302 def visit_array_type(self, name, info, element_type): 303 decl = gen_visit_decl(name) 304 defn = gen_visit_list(name, element_type) 305 if isinstance(element_type, QAPISchemaBuiltinType): 306 self._btin += decl 307 if do_builtins: 308 self.defn += defn 309 else: 310 self.decl += decl 311 self.defn += defn 312 313 def visit_object_type(self, name, info, base, members, variants): 314 # Nothing to do for the special empty builtin 315 if name == 'q_empty': 316 return 317 self.decl += gen_visit_members_decl(name) 318 self.defn += gen_visit_object_members(name, base, members, variants) 319 # TODO Worth changing the visitor signature, so we could 320 # directly use rather than repeat type.is_implicit()? 321 if not name.startswith('q_'): 322 # only explicit types need an allocating visit 323 self.decl += gen_visit_decl(name) 324 self.defn += gen_visit_object(name, base, members, variants) 325 326 def visit_alternate_type(self, name, info, variants): 327 self.decl += gen_visit_decl(name) 328 self.defn += gen_visit_alternate(name, variants) 329 330# If you link code generated from multiple schemata, you want only one 331# instance of the code for built-in types. Generate it only when 332# do_builtins, enabled by command line option -b. See also 333# QAPISchemaGenVisitVisitor.visit_end(). 334do_builtins = False 335 336(input_file, output_dir, do_c, do_h, prefix, opts) = \ 337 parse_command_line('b', ['builtins']) 338 339for o, a in opts: 340 if o in ('-b', '--builtins'): 341 do_builtins = True 342 343c_comment = ''' 344/* 345 * schema-defined QAPI visitor functions 346 * 347 * Copyright IBM, Corp. 2011 348 * 349 * Authors: 350 * Anthony Liguori <aliguori@us.ibm.com> 351 * 352 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 353 * See the COPYING.LIB file in the top-level directory. 354 * 355 */ 356''' 357h_comment = ''' 358/* 359 * schema-defined QAPI visitor functions 360 * 361 * Copyright IBM, Corp. 2011 362 * 363 * Authors: 364 * Anthony Liguori <aliguori@us.ibm.com> 365 * 366 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 367 * See the COPYING.LIB file in the top-level directory. 368 * 369 */ 370''' 371 372(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, 373 'qapi-visit.c', 'qapi-visit.h', 374 c_comment, h_comment) 375 376fdef.write(mcgen(''' 377#include "qemu/osdep.h" 378#include "qemu-common.h" 379#include "qapi/error.h" 380#include "%(prefix)sqapi-visit.h" 381''', 382 prefix=prefix)) 383 384fdecl.write(mcgen(''' 385#include "qapi/visitor.h" 386#include "qapi/qmp/qerror.h" 387#include "%(prefix)sqapi-types.h" 388 389''', 390 prefix=prefix)) 391 392schema = QAPISchema(input_file) 393gen = QAPISchemaGenVisitVisitor() 394schema.visit(gen) 395fdef.write(gen.defn) 396fdecl.write(gen.decl) 397 398close_output(fdef, fdecl) 399