1# 2# QAPI visitor generator 3# 4# Copyright IBM, Corp. 2011 5# Copyright (C) 2014-2015 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 ordereddict import OrderedDict 16from qapi import * 17import re 18 19implicit_structs = [] 20 21def generate_visit_implicit_struct(type): 22 global implicit_structs 23 if type in implicit_structs: 24 return '' 25 implicit_structs.append(type) 26 return mcgen(''' 27 28static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) 29{ 30 Error *err = NULL; 31 32 visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err); 33 if (!err) { 34 visit_type_%(c_type)s_fields(m, obj, errp); 35 visit_end_implicit_struct(m, &err); 36 } 37 error_propagate(errp, err); 38} 39''', 40 c_type=type_name(type)) 41 42def generate_visit_struct_fields(name, members, base = None): 43 ret = '' 44 45 if base: 46 ret += generate_visit_implicit_struct(base) 47 48 ret += mcgen(''' 49 50static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp) 51{ 52 Error *err = NULL; 53''', 54 name=c_name(name)) 55 push_indent() 56 57 if base: 58 ret += mcgen(''' 59visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err); 60if (err) { 61 goto out; 62} 63''', 64 type=type_name(base), c_name=c_name('base')) 65 66 for argname, argentry, optional in parse_args(members): 67 if optional: 68 ret += mcgen(''' 69visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err); 70if (!err && (*obj)->has_%(c_name)s) { 71''', 72 c_name=c_name(argname), name=argname) 73 push_indent() 74 75 ret += mcgen(''' 76visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); 77''', 78 type=type_name(argentry), c_name=c_name(argname), 79 name=argname) 80 81 if optional: 82 pop_indent() 83 ret += mcgen(''' 84} 85''') 86 ret += mcgen(''' 87if (err) { 88 goto out; 89} 90''') 91 92 pop_indent() 93 if re.search('^ *goto out\\;', ret, re.MULTILINE): 94 ret += mcgen(''' 95 96out: 97''') 98 ret += mcgen(''' 99 error_propagate(errp, err); 100} 101''') 102 return ret 103 104 105def generate_visit_struct_body(name): 106 ret = mcgen(''' 107 Error *err = NULL; 108 109 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); 110 if (!err) { 111 if (*obj) { 112 visit_type_%(c_name)s_fields(m, obj, errp); 113 } 114 visit_end_struct(m, &err); 115 } 116 error_propagate(errp, err); 117''', 118 name=name, c_name=c_name(name)) 119 120 return ret 121 122def generate_visit_struct(expr): 123 124 name = expr['struct'] 125 members = expr['data'] 126 base = expr.get('base') 127 128 ret = generate_visit_struct_fields(name, members, base) 129 130 ret += mcgen(''' 131 132void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) 133{ 134''', 135 name=c_name(name)) 136 137 ret += generate_visit_struct_body(name) 138 139 ret += mcgen(''' 140} 141''') 142 return ret 143 144def generate_visit_list(name): 145 return mcgen(''' 146 147void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp) 148{ 149 Error *err = NULL; 150 GenericList *i, **prev; 151 152 visit_start_list(m, name, &err); 153 if (err) { 154 goto out; 155 } 156 157 for (prev = (GenericList **)obj; 158 !err && (i = visit_next_list(m, prev, &err)) != NULL; 159 prev = &i) { 160 %(name)sList *native_i = (%(name)sList *)i; 161 visit_type_%(name)s(m, &native_i->value, NULL, &err); 162 } 163 164 error_propagate(errp, err); 165 err = NULL; 166 visit_end_list(m, &err); 167out: 168 error_propagate(errp, err); 169} 170''', 171 name=type_name(name)) 172 173def generate_visit_enum(name): 174 return mcgen(''' 175 176void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp) 177{ 178 visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); 179} 180''', 181 name=c_name(name)) 182 183def generate_visit_alternate(name, members): 184 ret = mcgen(''' 185 186void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) 187{ 188 Error *err = NULL; 189 190 visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); 191 if (err) { 192 goto out; 193 } 194 visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); 195 if (err) { 196 goto out_end; 197 } 198 switch ((*obj)->kind) { 199''', 200 name=c_name(name)) 201 202 # For alternate, always use the default enum type automatically generated 203 # as name + 'Kind' 204 disc_type = c_name(name) + 'Kind' 205 206 for key in members: 207 assert (members[key] in builtin_types.keys() 208 or find_struct(members[key]) 209 or find_union(members[key]) 210 or find_enum(members[key])), "Invalid alternate member" 211 212 enum_full_value = c_enum_const(disc_type, key) 213 ret += mcgen(''' 214 case %(enum_full_value)s: 215 visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); 216 break; 217''', 218 enum_full_value = enum_full_value, 219 c_type = type_name(members[key]), 220 c_name = c_name(key)) 221 222 ret += mcgen(''' 223 default: 224 abort(); 225 } 226out_end: 227 error_propagate(errp, err); 228 err = NULL; 229 visit_end_implicit_struct(m, &err); 230out: 231 error_propagate(errp, err); 232} 233''') 234 235 return ret 236 237 238def generate_visit_union(expr): 239 240 name = expr['union'] 241 members = expr['data'] 242 243 base = expr.get('base') 244 discriminator = expr.get('discriminator') 245 246 enum_define = discriminator_find_enum_define(expr) 247 if enum_define: 248 # Use the enum type as discriminator 249 ret = "" 250 disc_type = c_name(enum_define['enum_name']) 251 else: 252 # There will always be a discriminator in the C switch code, by default 253 # it is an enum type generated silently 254 ret = generate_visit_enum(name + 'Kind') 255 disc_type = c_name(name) + 'Kind' 256 257 if base: 258 assert discriminator 259 base_fields = find_struct(base)['data'].copy() 260 del base_fields[discriminator] 261 ret += generate_visit_struct_fields(name, base_fields) 262 263 if discriminator: 264 for key in members: 265 ret += generate_visit_implicit_struct(members[key]) 266 267 ret += mcgen(''' 268 269void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) 270{ 271 Error *err = NULL; 272 273 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); 274 if (err) { 275 goto out; 276 } 277 if (*obj) { 278''', 279 name=c_name(name)) 280 281 if base: 282 ret += mcgen(''' 283 visit_type_%(name)s_fields(m, obj, &err); 284 if (err) { 285 goto out_obj; 286 } 287''', 288 name=c_name(name)) 289 290 if not discriminator: 291 disc_key = "type" 292 else: 293 disc_key = discriminator 294 ret += mcgen(''' 295 visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); 296 if (err) { 297 goto out_obj; 298 } 299 if (!visit_start_union(m, !!(*obj)->data, &err) || err) { 300 goto out_obj; 301 } 302 switch ((*obj)->kind) { 303''', 304 disc_type = disc_type, 305 disc_key = disc_key) 306 307 for key in members: 308 if not discriminator: 309 fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' 310 else: 311 fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' 312 313 enum_full_value = c_enum_const(disc_type, key) 314 ret += mcgen(''' 315 case %(enum_full_value)s: 316 ''' + fmt + ''' 317 break; 318''', 319 enum_full_value = enum_full_value, 320 c_type=type_name(members[key]), 321 c_name=c_name(key)) 322 323 ret += mcgen(''' 324 default: 325 abort(); 326 } 327out_obj: 328 error_propagate(errp, err); 329 err = NULL; 330 visit_end_union(m, !!(*obj)->data, &err); 331 error_propagate(errp, err); 332 err = NULL; 333 } 334 visit_end_struct(m, &err); 335out: 336 error_propagate(errp, err); 337} 338''') 339 340 return ret 341 342def generate_declaration(name, builtin_type=False): 343 ret = "" 344 if not builtin_type: 345 name = c_name(name) 346 ret += mcgen(''' 347 348void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp); 349''', 350 name=name) 351 352 ret += mcgen(''' 353void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); 354''', 355 name=name) 356 357 return ret 358 359def generate_enum_declaration(name): 360 ret = mcgen(''' 361void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); 362''', 363 name=c_name(name)) 364 365 return ret 366 367def generate_decl_enum(name): 368 return mcgen(''' 369 370void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); 371''', 372 name=c_name(name)) 373 374do_builtins = False 375 376(input_file, output_dir, do_c, do_h, prefix, opts) = \ 377 parse_command_line("b", ["builtins"]) 378 379for o, a in opts: 380 if o in ("-b", "--builtins"): 381 do_builtins = True 382 383c_comment = ''' 384/* 385 * schema-defined QAPI visitor functions 386 * 387 * Copyright IBM, Corp. 2011 388 * 389 * Authors: 390 * Anthony Liguori <aliguori@us.ibm.com> 391 * 392 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 393 * See the COPYING.LIB file in the top-level directory. 394 * 395 */ 396''' 397h_comment = ''' 398/* 399 * schema-defined QAPI visitor functions 400 * 401 * Copyright IBM, Corp. 2011 402 * 403 * Authors: 404 * Anthony Liguori <aliguori@us.ibm.com> 405 * 406 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 407 * See the COPYING.LIB file in the top-level directory. 408 * 409 */ 410''' 411 412(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, 413 'qapi-visit.c', 'qapi-visit.h', 414 c_comment, h_comment) 415 416fdef.write(mcgen(''' 417#include "qemu-common.h" 418#include "%(prefix)sqapi-visit.h" 419''', 420 prefix = prefix)) 421 422fdecl.write(mcgen(''' 423#include "qapi/visitor.h" 424#include "%(prefix)sqapi-types.h" 425 426''', 427 prefix=prefix)) 428 429exprs = parse_schema(input_file) 430 431# to avoid header dependency hell, we always generate declarations 432# for built-in types in our header files and simply guard them 433fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL")) 434for typename in builtin_types.keys(): 435 fdecl.write(generate_declaration(typename, builtin_type=True)) 436fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) 437 438# ...this doesn't work for cases where we link in multiple objects that 439# have the functions defined, so we use -b option to provide control 440# over these cases 441if do_builtins: 442 for typename in builtin_types.keys(): 443 fdef.write(generate_visit_list(typename)) 444 445for expr in exprs: 446 if expr.has_key('struct'): 447 ret = generate_visit_struct(expr) 448 ret += generate_visit_list(expr['struct']) 449 fdef.write(ret) 450 451 ret = generate_declaration(expr['struct']) 452 fdecl.write(ret) 453 elif expr.has_key('union'): 454 ret = generate_visit_union(expr) 455 ret += generate_visit_list(expr['union']) 456 fdef.write(ret) 457 458 enum_define = discriminator_find_enum_define(expr) 459 ret = "" 460 if not enum_define: 461 ret = generate_decl_enum('%sKind' % expr['union']) 462 ret += generate_declaration(expr['union']) 463 fdecl.write(ret) 464 elif expr.has_key('alternate'): 465 ret = generate_visit_alternate(expr['alternate'], expr['data']) 466 ret += generate_visit_list(expr['alternate']) 467 fdef.write(ret) 468 469 ret = generate_decl_enum('%sKind' % expr['alternate']) 470 ret += generate_declaration(expr['alternate']) 471 fdecl.write(ret) 472 elif expr.has_key('enum'): 473 ret = generate_visit_list(expr['enum']) 474 ret += generate_visit_enum(expr['enum']) 475 fdef.write(ret) 476 477 ret = generate_decl_enum(expr['enum']) 478 ret += generate_enum_declaration(expr['enum']) 479 fdecl.write(ret) 480 481close_output(fdef, fdecl) 482