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