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