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