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