1""" 2QAPI types generator 3 4Copyright IBM, Corp. 2011 5Copyright (c) 2013-2018 Red Hat Inc. 6 7Authors: 8 Anthony Liguori <aliguori@us.ibm.com> 9 Michael Roth <mdroth@linux.vnet.ibm.com> 10 Markus Armbruster <armbru@redhat.com> 11 12This work is licensed under the terms of the GNU GPL, version 2. 13# See the COPYING file in the top-level directory. 14""" 15 16from qapi.common import * 17from qapi.gen import QAPISchemaModularCVisitor, ifcontext 18from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType 19 20 21# variants must be emitted before their container; track what has already 22# been output 23objects_seen = set() 24 25 26def gen_enum_lookup(name, members, prefix=None): 27 ret = mcgen(''' 28 29const QEnumLookup %(c_name)s_lookup = { 30 .array = (const char *const[]) { 31''', 32 c_name=c_name(name)) 33 for m in members: 34 ret += gen_if(m.ifcond) 35 index = c_enum_const(name, m.name, prefix) 36 ret += mcgen(''' 37 [%(index)s] = "%(name)s", 38''', 39 index=index, name=m.name) 40 ret += gen_endif(m.ifcond) 41 42 ret += mcgen(''' 43 }, 44 .size = %(max_index)s 45}; 46''', 47 max_index=c_enum_const(name, '_MAX', prefix)) 48 return ret 49 50 51def gen_enum(name, members, prefix=None): 52 # append automatically generated _MAX value 53 enum_members = members + [QAPISchemaEnumMember('_MAX', None)] 54 55 ret = mcgen(''' 56 57typedef enum %(c_name)s { 58''', 59 c_name=c_name(name)) 60 61 for m in enum_members: 62 ret += gen_if(m.ifcond) 63 ret += mcgen(''' 64 %(c_enum)s, 65''', 66 c_enum=c_enum_const(name, m.name, prefix)) 67 ret += gen_endif(m.ifcond) 68 69 ret += mcgen(''' 70} %(c_name)s; 71''', 72 c_name=c_name(name)) 73 74 ret += mcgen(''' 75 76#define %(c_name)s_str(val) \\ 77 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 78 79extern const QEnumLookup %(c_name)s_lookup; 80''', 81 c_name=c_name(name)) 82 return ret 83 84 85def gen_fwd_object_or_array(name): 86 return mcgen(''' 87 88typedef struct %(c_name)s %(c_name)s; 89''', 90 c_name=c_name(name)) 91 92 93def gen_array(name, element_type): 94 return mcgen(''' 95 96struct %(c_name)s { 97 %(c_name)s *next; 98 %(c_type)s value; 99}; 100''', 101 c_name=c_name(name), c_type=element_type.c_type()) 102 103 104def gen_struct_members(members): 105 ret = '' 106 for memb in members: 107 ret += gen_if(memb.ifcond) 108 if memb.optional: 109 ret += mcgen(''' 110 bool has_%(c_name)s; 111''', 112 c_name=c_name(memb.name)) 113 ret += mcgen(''' 114 %(c_type)s %(c_name)s; 115''', 116 c_type=memb.type.c_type(), c_name=c_name(memb.name)) 117 ret += gen_endif(memb.ifcond) 118 return ret 119 120 121def gen_object(name, ifcond, base, members, variants): 122 if name in objects_seen: 123 return '' 124 objects_seen.add(name) 125 126 ret = '' 127 if variants: 128 for v in variants.variants: 129 if isinstance(v.type, QAPISchemaObjectType): 130 ret += gen_object(v.type.name, v.type.ifcond, v.type.base, 131 v.type.local_members, v.type.variants) 132 133 ret += mcgen(''' 134 135''') 136 ret += gen_if(ifcond) 137 ret += mcgen(''' 138struct %(c_name)s { 139''', 140 c_name=c_name(name)) 141 142 if base: 143 if not base.is_implicit(): 144 ret += mcgen(''' 145 /* Members inherited from %(c_name)s: */ 146''', 147 c_name=base.c_name()) 148 ret += gen_struct_members(base.members) 149 if not base.is_implicit(): 150 ret += mcgen(''' 151 /* Own members: */ 152''') 153 ret += gen_struct_members(members) 154 155 if variants: 156 ret += gen_variants(variants) 157 158 # Make sure that all structs have at least one member; this avoids 159 # potential issues with attempting to malloc space for zero-length 160 # structs in C, and also incompatibility with C++ (where an empty 161 # struct is size 1). 162 if (not base or base.is_empty()) and not members and not variants: 163 ret += mcgen(''' 164 char qapi_dummy_for_empty_struct; 165''') 166 167 ret += mcgen(''' 168}; 169''') 170 ret += gen_endif(ifcond) 171 172 return ret 173 174 175def gen_upcast(name, base): 176 # C makes const-correctness ugly. We have to cast away const to let 177 # this function work for both const and non-const obj. 178 return mcgen(''' 179 180static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj) 181{ 182 return (%(base)s *)obj; 183} 184''', 185 c_name=c_name(name), base=base.c_name()) 186 187 188def gen_variants(variants): 189 ret = mcgen(''' 190 union { /* union tag is @%(c_name)s */ 191''', 192 c_name=c_name(variants.tag_member.name)) 193 194 for var in variants.variants: 195 if var.type.name == 'q_empty': 196 continue 197 ret += gen_if(var.ifcond) 198 ret += mcgen(''' 199 %(c_type)s %(c_name)s; 200''', 201 c_type=var.type.c_unboxed_type(), 202 c_name=c_name(var.name)) 203 ret += gen_endif(var.ifcond) 204 205 ret += mcgen(''' 206 } u; 207''') 208 209 return ret 210 211 212def gen_type_cleanup_decl(name): 213 ret = mcgen(''' 214 215void qapi_free_%(c_name)s(%(c_name)s *obj); 216''', 217 c_name=c_name(name)) 218 return ret 219 220 221def gen_type_cleanup(name): 222 ret = mcgen(''' 223 224void qapi_free_%(c_name)s(%(c_name)s *obj) 225{ 226 Visitor *v; 227 228 if (!obj) { 229 return; 230 } 231 232 v = qapi_dealloc_visitor_new(); 233 visit_type_%(c_name)s(v, NULL, &obj, NULL); 234 visit_free(v); 235} 236''', 237 c_name=c_name(name)) 238 return ret 239 240 241class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): 242 243 def __init__(self, prefix): 244 super().__init__( 245 prefix, 'qapi-types', ' * Schema-defined QAPI types', 246 ' * Built-in QAPI types', __doc__) 247 248 def _begin_system_module(self, name): 249 self._genc.preamble_add(mcgen(''' 250#include "qemu/osdep.h" 251#include "qapi/dealloc-visitor.h" 252#include "qapi/qapi-builtin-types.h" 253#include "qapi/qapi-builtin-visit.h" 254''')) 255 self._genh.preamble_add(mcgen(''' 256#include "qapi/util.h" 257''')) 258 259 def _begin_user_module(self, name): 260 types = self._module_basename('qapi-types', name) 261 visit = self._module_basename('qapi-visit', name) 262 self._genc.preamble_add(mcgen(''' 263#include "qemu/osdep.h" 264#include "qapi/dealloc-visitor.h" 265#include "%(types)s.h" 266#include "%(visit)s.h" 267''', 268 types=types, visit=visit)) 269 self._genh.preamble_add(mcgen(''' 270#include "qapi/qapi-builtin-types.h" 271''')) 272 273 def visit_begin(self, schema): 274 # gen_object() is recursive, ensure it doesn't visit the empty type 275 objects_seen.add(schema.the_empty_object_type.name) 276 277 def _gen_type_cleanup(self, name): 278 self._genh.add(gen_type_cleanup_decl(name)) 279 self._genc.add(gen_type_cleanup(name)) 280 281 def visit_enum_type(self, name, info, ifcond, members, prefix): 282 with ifcontext(ifcond, self._genh, self._genc): 283 self._genh.preamble_add(gen_enum(name, members, prefix)) 284 self._genc.add(gen_enum_lookup(name, members, prefix)) 285 286 def visit_array_type(self, name, info, ifcond, element_type): 287 with ifcontext(ifcond, self._genh, self._genc): 288 self._genh.preamble_add(gen_fwd_object_or_array(name)) 289 self._genh.add(gen_array(name, element_type)) 290 self._gen_type_cleanup(name) 291 292 def visit_object_type(self, name, info, ifcond, base, members, variants, 293 features): 294 # Nothing to do for the special empty builtin 295 if name == 'q_empty': 296 return 297 with ifcontext(ifcond, self._genh): 298 self._genh.preamble_add(gen_fwd_object_or_array(name)) 299 self._genh.add(gen_object(name, ifcond, base, members, variants)) 300 with ifcontext(ifcond, self._genh, self._genc): 301 if base and not base.is_implicit(): 302 self._genh.add(gen_upcast(name, base)) 303 # TODO Worth changing the visitor signature, so we could 304 # directly use rather than repeat type.is_implicit()? 305 if not name.startswith('q_'): 306 # implicit types won't be directly allocated/freed 307 self._gen_type_cleanup(name) 308 309 def visit_alternate_type(self, name, info, ifcond, variants): 310 with ifcontext(ifcond, self._genh): 311 self._genh.preamble_add(gen_fwd_object_or_array(name)) 312 self._genh.add(gen_object(name, ifcond, None, 313 [variants.tag_member], variants)) 314 with ifcontext(ifcond, self._genh, self._genc): 315 self._gen_type_cleanup(name) 316 317 318def gen_types(schema, output_dir, prefix, opt_builtins): 319 vis = QAPISchemaGenTypeVisitor(prefix) 320 schema.visit(vis) 321 vis.write(output_dir, opt_builtins) 322