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