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 * 16import re 17 18# visit_type_FOO_implicit() is emitted as needed; track if it has already 19# been output. 20implicit_structs_seen = set() 21 22# visit_type_FOO_fields() is always emitted; track if a forward declaration 23# or implementation has already been output. 24struct_fields_seen = set() 25 26 27def gen_visit_decl(name, scalar=False): 28 c_type = c_name(name) + ' *' 29 if not scalar: 30 c_type += '*' 31 return mcgen(''' 32void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp); 33''', 34 c_name=c_name(name), c_type=c_type) 35 36 37def gen_visit_fields_decl(typ): 38 ret = '' 39 if typ.name not in struct_fields_seen: 40 ret += mcgen(''' 41 42static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp); 43''', 44 c_type=typ.c_name()) 45 struct_fields_seen.add(typ.name) 46 return ret 47 48 49def gen_visit_implicit_struct(typ): 50 if typ in implicit_structs_seen: 51 return '' 52 implicit_structs_seen.add(typ) 53 54 ret = gen_visit_fields_decl(typ) 55 56 ret += mcgen(''' 57 58static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp) 59{ 60 Error *err = NULL; 61 62 visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err); 63 if (!err) { 64 visit_type_%(c_type)s_fields(v, obj, errp); 65 visit_end_implicit_struct(v); 66 } 67 error_propagate(errp, err); 68} 69''', 70 c_type=typ.c_name()) 71 return ret 72 73 74def gen_visit_struct_fields(name, base, members, variants): 75 ret = '' 76 77 if base: 78 ret += gen_visit_fields_decl(base) 79 if variants: 80 for var in variants.variants: 81 # Ugly special case for simple union TODO get rid of it 82 if not var.simple_union_type(): 83 ret += gen_visit_implicit_struct(var.type) 84 85 struct_fields_seen.add(name) 86 ret += mcgen(''' 87 88static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp) 89{ 90 Error *err = NULL; 91 92''', 93 c_name=c_name(name)) 94 95 if base: 96 ret += mcgen(''' 97 visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err); 98''', 99 c_type=base.c_name()) 100 ret += gen_err_check() 101 102 ret += gen_visit_fields(members, prefix='(*obj)->') 103 104 if variants: 105 ret += mcgen(''' 106 if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) { 107 goto out; 108 } 109 switch ((*obj)->%(c_name)s) { 110''', 111 c_name=c_name(variants.tag_member.name)) 112 113 for var in variants.variants: 114 # TODO ugly special case for simple union 115 simple_union_type = var.simple_union_type() 116 ret += mcgen(''' 117 case %(case)s: 118''', 119 case=c_enum_const(variants.tag_member.type.name, 120 var.name, 121 variants.tag_member.type.prefix)) 122 if simple_union_type: 123 ret += mcgen(''' 124 visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err); 125''', 126 c_type=simple_union_type.c_name(), 127 c_name=c_name(var.name)) 128 else: 129 ret += mcgen(''' 130 visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err); 131''', 132 c_type=var.type.c_name(), 133 c_name=c_name(var.name)) 134 ret += mcgen(''' 135 break; 136''') 137 138 ret += mcgen(''' 139 default: 140 abort(); 141 } 142''') 143 144 # 'goto out' produced for base, by gen_visit_fields() for each member, 145 # and if variants were present 146 if base or members or variants: 147 ret += mcgen(''' 148 149out: 150''') 151 ret += mcgen(''' 152 error_propagate(errp, err); 153} 154''') 155 return ret 156 157 158def gen_visit_list(name, element_type): 159 # FIXME: if *obj is NULL on entry, and the first visit_next_list() 160 # assigns to *obj, while a later one fails, we should clean up *obj 161 # rather than leaving it non-NULL. As currently written, the caller must 162 # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList. 163 return mcgen(''' 164 165void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 166{ 167 Error *err = NULL; 168 GenericList *i, **prev; 169 170 visit_start_list(v, name, &err); 171 if (err) { 172 goto out; 173 } 174 175 for (prev = (GenericList **)obj; 176 !err && (i = visit_next_list(v, prev)) != NULL; 177 prev = &i) { 178 %(c_name)s *native_i = (%(c_name)s *)i; 179 visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err); 180 } 181 182 visit_end_list(v); 183out: 184 error_propagate(errp, err); 185} 186''', 187 c_name=c_name(name), c_elt_type=element_type.c_name()) 188 189 190def gen_visit_enum(name): 191 return mcgen(''' 192 193void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp) 194{ 195 int value = *obj; 196 visit_type_enum(v, name, &value, %(c_name)s_lookup, errp); 197 *obj = value; 198} 199''', 200 c_name=c_name(name)) 201 202 203def gen_visit_alternate(name, variants): 204 promote_int = 'true' 205 for var in variants.variants: 206 if var.type.alternate_qtype() == 'QTYPE_QINT': 207 promote_int = 'false' 208 209 ret = mcgen(''' 210 211void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 212{ 213 Error *err = NULL; 214 215 visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err); 216 if (err) { 217 goto out; 218 } 219 visit_get_next_type(v, name, &(*obj)->type, %(promote_int)s, &err); 220 if (err) { 221 goto out_obj; 222 } 223 switch ((*obj)->type) { 224''', 225 c_name=c_name(name), promote_int=promote_int) 226 227 for var in variants.variants: 228 ret += mcgen(''' 229 case %(case)s: 230 visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err); 231 break; 232''', 233 case=var.type.alternate_qtype(), 234 c_type=var.type.c_name(), 235 c_name=c_name(var.name)) 236 237 ret += mcgen(''' 238 default: 239 error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 240 "%(name)s"); 241 } 242out_obj: 243 visit_end_implicit_struct(v); 244out: 245 error_propagate(errp, err); 246} 247''', 248 name=name) 249 250 return ret 251 252 253def gen_visit_object(name, base, members, variants): 254 ret = gen_visit_struct_fields(name, base, members, variants) 255 256 # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to 257 # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj 258 # rather than leaving it non-NULL. As currently written, the caller must 259 # call qapi_free_FOO() to avoid a memory leak of the partial FOO. 260 ret += mcgen(''' 261 262void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 263{ 264 Error *err = NULL; 265 266 visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err); 267 if (err) { 268 goto out; 269 } 270 if (!*obj) { 271 goto out_obj; 272 } 273 visit_type_%(c_name)s_fields(v, obj, &err); 274 error_propagate(errp, err); 275 err = NULL; 276out_obj: 277 visit_end_struct(v, &err); 278out: 279 error_propagate(errp, err); 280} 281''', 282 c_name=c_name(name)) 283 284 return ret 285 286 287class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): 288 def __init__(self): 289 self.decl = None 290 self.defn = None 291 self._btin = None 292 293 def visit_begin(self, schema): 294 self.decl = '' 295 self.defn = '' 296 self._btin = guardstart('QAPI_VISIT_BUILTIN') 297 298 def visit_end(self): 299 # To avoid header dependency hell, we always generate 300 # declarations for built-in types in our header files and 301 # simply guard them. See also do_builtins (command line 302 # option -b). 303 self._btin += guardend('QAPI_VISIT_BUILTIN') 304 self.decl = self._btin + self.decl 305 self._btin = None 306 307 def visit_needed(self, entity): 308 # Visit everything except implicit objects 309 return not (entity.is_implicit() and 310 isinstance(entity, QAPISchemaObjectType)) 311 312 def visit_enum_type(self, name, info, values, prefix): 313 # Special case for our lone builtin enum type 314 # TODO use something cleaner than existence of info 315 if not info: 316 self._btin += gen_visit_decl(name, scalar=True) 317 if do_builtins: 318 self.defn += gen_visit_enum(name) 319 else: 320 self.decl += gen_visit_decl(name, scalar=True) 321 self.defn += gen_visit_enum(name) 322 323 def visit_array_type(self, name, info, element_type): 324 decl = gen_visit_decl(name) 325 defn = gen_visit_list(name, element_type) 326 if isinstance(element_type, QAPISchemaBuiltinType): 327 self._btin += decl 328 if do_builtins: 329 self.defn += defn 330 else: 331 self.decl += decl 332 self.defn += defn 333 334 def visit_object_type(self, name, info, base, members, variants): 335 self.decl += gen_visit_decl(name) 336 self.defn += gen_visit_object(name, base, members, variants) 337 338 def visit_alternate_type(self, name, info, variants): 339 self.decl += gen_visit_decl(name) 340 self.defn += gen_visit_alternate(name, variants) 341 342# If you link code generated from multiple schemata, you want only one 343# instance of the code for built-in types. Generate it only when 344# do_builtins, enabled by command line option -b. See also 345# QAPISchemaGenVisitVisitor.visit_end(). 346do_builtins = False 347 348(input_file, output_dir, do_c, do_h, prefix, opts) = \ 349 parse_command_line("b", ["builtins"]) 350 351for o, a in opts: 352 if o in ("-b", "--builtins"): 353 do_builtins = True 354 355c_comment = ''' 356/* 357 * schema-defined QAPI visitor functions 358 * 359 * Copyright IBM, Corp. 2011 360 * 361 * Authors: 362 * Anthony Liguori <aliguori@us.ibm.com> 363 * 364 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 365 * See the COPYING.LIB file in the top-level directory. 366 * 367 */ 368''' 369h_comment = ''' 370/* 371 * schema-defined QAPI visitor functions 372 * 373 * Copyright IBM, Corp. 2011 374 * 375 * Authors: 376 * Anthony Liguori <aliguori@us.ibm.com> 377 * 378 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 379 * See the COPYING.LIB file in the top-level directory. 380 * 381 */ 382''' 383 384(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, 385 'qapi-visit.c', 'qapi-visit.h', 386 c_comment, h_comment) 387 388fdef.write(mcgen(''' 389#include "qemu/osdep.h" 390#include "qemu-common.h" 391#include "%(prefix)sqapi-visit.h" 392''', 393 prefix=prefix)) 394 395fdecl.write(mcgen(''' 396#include "qapi/visitor.h" 397#include "qapi/qmp/qerror.h" 398#include "%(prefix)sqapi-types.h" 399 400''', 401 prefix=prefix)) 402 403schema = QAPISchema(input_file) 404gen = QAPISchemaGenVisitVisitor() 405schema.visit(gen) 406fdef.write(gen.defn) 407fdecl.write(gen.decl) 408 409close_output(fdef, fdecl) 410