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 ret = mcgen(''' 165 166void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 167{ 168 Error *err = NULL; 169 170 visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj), 171 &err); 172 if (err) { 173 goto out; 174 } 175 if (!*obj) { 176 goto out_obj; 177 } 178 switch ((*obj)->type) { 179''', 180 c_name=c_name(name)) 181 182 for var in variants.variants: 183 ret += mcgen(''' 184 case %(case)s: 185''', 186 case=var.type.alternate_qtype()) 187 if isinstance(var.type, QAPISchemaObjectType): 188 ret += mcgen(''' 189 visit_start_struct(v, name, NULL, 0, &err); 190 if (err) { 191 break; 192 } 193 visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err); 194 if (!err) { 195 visit_check_struct(v, &err); 196 } 197 visit_end_struct(v, NULL); 198''', 199 c_type=var.type.c_name(), 200 c_name=c_name(var.name)) 201 else: 202 ret += mcgen(''' 203 visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err); 204''', 205 c_type=var.type.c_name(), 206 c_name=c_name(var.name)) 207 ret += mcgen(''' 208 break; 209''') 210 211 ret += mcgen(''' 212 case QTYPE_NONE: 213 abort(); 214 default: 215 error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 216 "%(name)s"); 217 } 218out_obj: 219 visit_end_alternate(v, (void **)obj); 220 if (err && visit_is_input(v)) { 221 qapi_free_%(c_name)s(*obj); 222 *obj = NULL; 223 } 224out: 225 error_propagate(errp, err); 226} 227''', 228 name=name, c_name=c_name(name)) 229 230 return ret 231 232 233def gen_visit_object(name, base, members, variants): 234 return mcgen(''' 235 236void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp) 237{ 238 Error *err = NULL; 239 240 visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err); 241 if (err) { 242 goto out; 243 } 244 if (!*obj) { 245 goto out_obj; 246 } 247 visit_type_%(c_name)s_members(v, *obj, &err); 248 if (err) { 249 goto out_obj; 250 } 251 visit_check_struct(v, &err); 252out_obj: 253 visit_end_struct(v, (void **)obj); 254 if (err && visit_is_input(v)) { 255 qapi_free_%(c_name)s(*obj); 256 *obj = NULL; 257 } 258out: 259 error_propagate(errp, err); 260} 261''', 262 c_name=c_name(name)) 263 264 265class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): 266 def __init__(self): 267 self.decl = None 268 self.defn = None 269 self._btin = None 270 271 def visit_begin(self, schema): 272 self.decl = '' 273 self.defn = '' 274 self._btin = guardstart('QAPI_VISIT_BUILTIN') 275 276 def visit_end(self): 277 # To avoid header dependency hell, we always generate 278 # declarations for built-in types in our header files and 279 # simply guard them. See also do_builtins (command line 280 # option -b). 281 self._btin += guardend('QAPI_VISIT_BUILTIN') 282 self.decl = self._btin + self.decl 283 self._btin = None 284 285 def visit_enum_type(self, name, info, values, prefix): 286 # Special case for our lone builtin enum type 287 # TODO use something cleaner than existence of info 288 if not info: 289 self._btin += gen_visit_decl(name, scalar=True) 290 if do_builtins: 291 self.defn += gen_visit_enum(name) 292 else: 293 self.decl += gen_visit_decl(name, scalar=True) 294 self.defn += gen_visit_enum(name) 295 296 def visit_array_type(self, name, info, element_type): 297 decl = gen_visit_decl(name) 298 defn = gen_visit_list(name, element_type) 299 if isinstance(element_type, QAPISchemaBuiltinType): 300 self._btin += decl 301 if do_builtins: 302 self.defn += defn 303 else: 304 self.decl += decl 305 self.defn += defn 306 307 def visit_object_type(self, name, info, base, members, variants): 308 # Nothing to do for the special empty builtin 309 if name == 'q_empty': 310 return 311 self.decl += gen_visit_members_decl(name) 312 self.defn += gen_visit_object_members(name, base, members, variants) 313 # TODO Worth changing the visitor signature, so we could 314 # directly use rather than repeat type.is_implicit()? 315 if not name.startswith('q_'): 316 # only explicit types need an allocating visit 317 self.decl += gen_visit_decl(name) 318 self.defn += gen_visit_object(name, base, members, variants) 319 320 def visit_alternate_type(self, name, info, variants): 321 self.decl += gen_visit_decl(name) 322 self.defn += gen_visit_alternate(name, variants) 323 324# If you link code generated from multiple schemata, you want only one 325# instance of the code for built-in types. Generate it only when 326# do_builtins, enabled by command line option -b. See also 327# QAPISchemaGenVisitVisitor.visit_end(). 328do_builtins = False 329 330(input_file, output_dir, do_c, do_h, prefix, opts) = \ 331 parse_command_line('b', ['builtins']) 332 333for o, a in opts: 334 if o in ('-b', '--builtins'): 335 do_builtins = True 336 337c_comment = ''' 338/* 339 * schema-defined QAPI visitor functions 340 * 341 * Copyright IBM, Corp. 2011 342 * 343 * Authors: 344 * Anthony Liguori <aliguori@us.ibm.com> 345 * 346 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 347 * See the COPYING.LIB file in the top-level directory. 348 * 349 */ 350''' 351h_comment = ''' 352/* 353 * schema-defined QAPI visitor functions 354 * 355 * Copyright IBM, Corp. 2011 356 * 357 * Authors: 358 * Anthony Liguori <aliguori@us.ibm.com> 359 * 360 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 361 * See the COPYING.LIB file in the top-level directory. 362 * 363 */ 364''' 365 366(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, 367 'qapi-visit.c', 'qapi-visit.h', 368 c_comment, h_comment) 369 370fdef.write(mcgen(''' 371#include "qemu/osdep.h" 372#include "qemu-common.h" 373#include "qapi/error.h" 374#include "%(prefix)sqapi-visit.h" 375''', 376 prefix=prefix)) 377 378fdecl.write(mcgen(''' 379#include "qapi/visitor.h" 380#include "qapi/qmp/qerror.h" 381#include "%(prefix)sqapi-types.h" 382 383''', 384 prefix=prefix)) 385 386schema = QAPISchema(input_file) 387gen = QAPISchemaGenVisitVisitor() 388schema.visit(gen) 389fdef.write(gen.defn) 390fdecl.write(gen.decl) 391 392close_output(fdef, fdecl) 393