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