| """ | |
| QAPI types generator | |
| Copyright IBM, Corp. 2011 | |
| Copyright (c) 2013-2018 Red Hat Inc. | |
| Authors: | |
| Anthony Liguori <aliguori@us.ibm.com> | |
| Michael Roth <mdroth@linux.vnet.ibm.com> | |
| Markus Armbruster <armbru@redhat.com> | |
| This work is licensed under the terms of the GNU GPL, version 2. | |
| # See the COPYING file in the top-level directory. | |
| """ | |
| from typing import List, Optional | |
| from .common import c_enum_const, c_name, mcgen | |
| from .gen import ( | |
| QAPISchemaModularCVisitor, | |
| gen_special_features, | |
| ifcontext, | |
| ) | |
| from .schema import ( | |
| QAPISchema, | |
| QAPISchemaEnumMember, | |
| QAPISchemaFeature, | |
| QAPISchemaIfCond, | |
| QAPISchemaObjectType, | |
| QAPISchemaObjectTypeMember, | |
| QAPISchemaType, | |
| QAPISchemaVariants, | |
| ) | |
| from .source import QAPISourceInfo | |
| # variants must be emitted before their container; track what has already | |
| # been output | |
| objects_seen = set() | |
| def gen_enum_lookup(name: str, | |
| members: List[QAPISchemaEnumMember], | |
| prefix: Optional[str] = None) -> str: | |
| max_index = c_enum_const(name, '_MAX', prefix) | |
| feats = '' | |
| ret = mcgen(''' | |
| const QEnumLookup %(c_name)s_lookup = { | |
| .array = (const char *const[]) { | |
| ''', | |
| c_name=c_name(name)) | |
| for memb in members: | |
| ret += memb.ifcond.gen_if() | |
| index = c_enum_const(name, memb.name, prefix) | |
| ret += mcgen(''' | |
| [%(index)s] = "%(name)s", | |
| ''', | |
| index=index, name=memb.name) | |
| ret += memb.ifcond.gen_endif() | |
| special_features = gen_special_features(memb.features) | |
| if special_features != '0': | |
| feats += mcgen(''' | |
| [%(index)s] = %(special_features)s, | |
| ''', | |
| index=index, special_features=special_features) | |
| if feats: | |
| ret += mcgen(''' | |
| }, | |
| .special_features = (const unsigned char[%(max_index)s]) { | |
| ''', | |
| max_index=max_index) | |
| ret += feats | |
| ret += mcgen(''' | |
| }, | |
| .size = %(max_index)s | |
| }; | |
| ''', | |
| max_index=max_index) | |
| return ret | |
| def gen_enum(name: str, | |
| members: List[QAPISchemaEnumMember], | |
| prefix: Optional[str] = None) -> str: | |
| # append automatically generated _MAX value | |
| enum_members = members + [QAPISchemaEnumMember('_MAX', None)] | |
| ret = mcgen(''' | |
| typedef enum %(c_name)s { | |
| ''', | |
| c_name=c_name(name)) | |
| for memb in enum_members: | |
| ret += memb.ifcond.gen_if() | |
| ret += mcgen(''' | |
| %(c_enum)s, | |
| ''', | |
| c_enum=c_enum_const(name, memb.name, prefix)) | |
| ret += memb.ifcond.gen_endif() | |
| ret += mcgen(''' | |
| } %(c_name)s; | |
| ''', | |
| c_name=c_name(name)) | |
| ret += mcgen(''' | |
| #define %(c_name)s_str(val) \\ | |
| qapi_enum_lookup(&%(c_name)s_lookup, (val)) | |
| extern const QEnumLookup %(c_name)s_lookup; | |
| ''', | |
| c_name=c_name(name)) | |
| return ret | |
| def gen_fwd_object_or_array(name: str) -> str: | |
| return mcgen(''' | |
| typedef struct %(c_name)s %(c_name)s; | |
| ''', | |
| c_name=c_name(name)) | |
| def gen_array(name: str, element_type: QAPISchemaType) -> str: | |
| return mcgen(''' | |
| struct %(c_name)s { | |
| %(c_name)s *next; | |
| %(c_type)s value; | |
| }; | |
| ''', | |
| c_name=c_name(name), c_type=element_type.c_type()) | |
| def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: | |
| ret = '' | |
| for memb in members: | |
| ret += memb.ifcond.gen_if() | |
| if memb.need_has(): | |
| ret += mcgen(''' | |
| bool has_%(c_name)s; | |
| ''', | |
| c_name=c_name(memb.name)) | |
| ret += mcgen(''' | |
| %(c_type)s %(c_name)s; | |
| ''', | |
| c_type=memb.type.c_type(), c_name=c_name(memb.name)) | |
| ret += memb.ifcond.gen_endif() | |
| return ret | |
| def gen_object(name: str, ifcond: QAPISchemaIfCond, | |
| base: Optional[QAPISchemaObjectType], | |
| members: List[QAPISchemaObjectTypeMember], | |
| variants: Optional[QAPISchemaVariants]) -> str: | |
| if name in objects_seen: | |
| return '' | |
| objects_seen.add(name) | |
| ret = '' | |
| for var in variants.variants if variants else (): | |
| obj = var.type | |
| if not isinstance(obj, QAPISchemaObjectType): | |
| continue | |
| ret += gen_object(obj.name, obj.ifcond, obj.base, | |
| obj.local_members, obj.variants) | |
| ret += mcgen(''' | |
| ''') | |
| ret += ifcond.gen_if() | |
| ret += mcgen(''' | |
| struct %(c_name)s { | |
| ''', | |
| c_name=c_name(name)) | |
| if base: | |
| if not base.is_implicit(): | |
| ret += mcgen(''' | |
| /* Members inherited from %(c_name)s: */ | |
| ''', | |
| c_name=base.c_name()) | |
| ret += gen_struct_members(base.members) | |
| if not base.is_implicit(): | |
| ret += mcgen(''' | |
| /* Own members: */ | |
| ''') | |
| ret += gen_struct_members(members) | |
| if variants: | |
| ret += gen_variants(variants) | |
| # Make sure that all structs have at least one member; this avoids | |
| # potential issues with attempting to malloc space for zero-length | |
| # structs in C, and also incompatibility with C++ (where an empty | |
| # struct is size 1). | |
| if (not base or base.is_empty()) and not members and not variants: | |
| ret += mcgen(''' | |
| char qapi_dummy_for_empty_struct; | |
| ''') | |
| ret += mcgen(''' | |
| }; | |
| ''') | |
| ret += ifcond.gen_endif() | |
| return ret | |
| def gen_upcast(name: str, base: QAPISchemaObjectType) -> str: | |
| # C makes const-correctness ugly. We have to cast away const to let | |
| # this function work for both const and non-const obj. | |
| return mcgen(''' | |
| static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj) | |
| { | |
| return (%(base)s *)obj; | |
| } | |
| ''', | |
| c_name=c_name(name), base=base.c_name()) | |
| def gen_variants(variants: QAPISchemaVariants) -> str: | |
| ret = mcgen(''' | |
| union { /* union tag is @%(c_name)s */ | |
| ''', | |
| c_name=c_name(variants.tag_member.name)) | |
| for var in variants.variants: | |
| if var.type.name == 'q_empty': | |
| continue | |
| ret += var.ifcond.gen_if() | |
| ret += mcgen(''' | |
| %(c_type)s %(c_name)s; | |
| ''', | |
| c_type=var.type.c_unboxed_type(), | |
| c_name=c_name(var.name)) | |
| ret += var.ifcond.gen_endif() | |
| ret += mcgen(''' | |
| } u; | |
| ''') | |
| return ret | |
| def gen_type_cleanup_decl(name: str) -> str: | |
| ret = mcgen(''' | |
| void qapi_free_%(c_name)s(%(c_name)s *obj); | |
| G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s) | |
| ''', | |
| c_name=c_name(name)) | |
| return ret | |
| def gen_type_cleanup(name: str) -> str: | |
| ret = mcgen(''' | |
| void qapi_free_%(c_name)s(%(c_name)s *obj) | |
| { | |
| Visitor *v; | |
| if (!obj) { | |
| return; | |
| } | |
| v = qapi_dealloc_visitor_new(); | |
| visit_type_%(c_name)s(v, NULL, &obj, NULL); | |
| visit_free(v); | |
| } | |
| ''', | |
| c_name=c_name(name)) | |
| return ret | |
| class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): | |
| def __init__(self, prefix: str): | |
| super().__init__( | |
| prefix, 'qapi-types', ' * Schema-defined QAPI types', | |
| ' * Built-in QAPI types', __doc__) | |
| def _begin_builtin_module(self) -> None: | |
| self._genc.preamble_add(mcgen(''' | |
| #include "qemu/osdep.h" | |
| #include "qapi/dealloc-visitor.h" | |
| #include "qapi/qapi-builtin-types.h" | |
| #include "qapi/qapi-builtin-visit.h" | |
| ''')) | |
| self._genh.preamble_add(mcgen(''' | |
| #include "qapi/util.h" | |
| ''')) | |
| def _begin_user_module(self, name: str) -> None: | |
| types = self._module_basename('qapi-types', name) | |
| visit = self._module_basename('qapi-visit', name) | |
| self._genc.preamble_add(mcgen(''' | |
| #include "qemu/osdep.h" | |
| #include "qapi/dealloc-visitor.h" | |
| #include "%(types)s.h" | |
| #include "%(visit)s.h" | |
| ''', | |
| types=types, visit=visit)) | |
| self._genh.preamble_add(mcgen(''' | |
| #include "qapi/qapi-builtin-types.h" | |
| ''')) | |
| def visit_begin(self, schema: QAPISchema) -> None: | |
| # gen_object() is recursive, ensure it doesn't visit the empty type | |
| objects_seen.add(schema.the_empty_object_type.name) | |
| def _gen_type_cleanup(self, name: str) -> None: | |
| self._genh.add(gen_type_cleanup_decl(name)) | |
| self._genc.add(gen_type_cleanup(name)) | |
| def visit_enum_type(self, | |
| name: str, | |
| info: Optional[QAPISourceInfo], | |
| ifcond: QAPISchemaIfCond, | |
| features: List[QAPISchemaFeature], | |
| members: List[QAPISchemaEnumMember], | |
| prefix: Optional[str]) -> None: | |
| with ifcontext(ifcond, self._genh, self._genc): | |
| self._genh.preamble_add(gen_enum(name, members, prefix)) | |
| self._genc.add(gen_enum_lookup(name, members, prefix)) | |
| def visit_array_type(self, | |
| name: str, | |
| info: Optional[QAPISourceInfo], | |
| ifcond: QAPISchemaIfCond, | |
| element_type: QAPISchemaType) -> None: | |
| with ifcontext(ifcond, self._genh, self._genc): | |
| self._genh.preamble_add(gen_fwd_object_or_array(name)) | |
| self._genh.add(gen_array(name, element_type)) | |
| self._gen_type_cleanup(name) | |
| def visit_object_type(self, | |
| name: str, | |
| info: Optional[QAPISourceInfo], | |
| ifcond: QAPISchemaIfCond, | |
| features: List[QAPISchemaFeature], | |
| base: Optional[QAPISchemaObjectType], | |
| members: List[QAPISchemaObjectTypeMember], | |
| variants: Optional[QAPISchemaVariants]) -> None: | |
| # Nothing to do for the special empty builtin | |
| if name == 'q_empty': | |
| return | |
| with ifcontext(ifcond, self._genh): | |
| self._genh.preamble_add(gen_fwd_object_or_array(name)) | |
| self._genh.add(gen_object(name, ifcond, base, members, variants)) | |
| with ifcontext(ifcond, self._genh, self._genc): | |
| if base and not base.is_implicit(): | |
| self._genh.add(gen_upcast(name, base)) | |
| # TODO Worth changing the visitor signature, so we could | |
| # directly use rather than repeat type.is_implicit()? | |
| if not name.startswith('q_'): | |
| # implicit types won't be directly allocated/freed | |
| self._gen_type_cleanup(name) | |
| def visit_alternate_type(self, | |
| name: str, | |
| info: Optional[QAPISourceInfo], | |
| ifcond: QAPISchemaIfCond, | |
| features: List[QAPISchemaFeature], | |
| variants: QAPISchemaVariants) -> None: | |
| with ifcontext(ifcond, self._genh): | |
| self._genh.preamble_add(gen_fwd_object_or_array(name)) | |
| self._genh.add(gen_object(name, ifcond, None, | |
| [variants.tag_member], variants)) | |
| with ifcontext(ifcond, self._genh, self._genc): | |
| self._gen_type_cleanup(name) | |
| def gen_types(schema: QAPISchema, | |
| output_dir: str, | |
| prefix: str, | |
| opt_builtins: bool) -> None: | |
| vis = QAPISchemaGenTypeVisitor(prefix) | |
| schema.visit(vis) | |
| vis.write(output_dir, opt_builtins) |