|  | """ | 
|  | 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, | 
|  | QAPISchemaAlternatives, | 
|  | QAPISchemaBranches, | 
|  | 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.branches) | 
|  |  | 
|  | 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], | 
|  | branches: Optional[QAPISchemaBranches]) -> 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, branches)) | 
|  | 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], | 
|  | alternatives: QAPISchemaAlternatives) -> None: | 
|  | with ifcontext(ifcond, self._genh): | 
|  | self._genh.preamble_add(gen_fwd_object_or_array(name)) | 
|  | self._genh.add(gen_object(name, ifcond, None, | 
|  | [alternatives.tag_member], alternatives)) | 
|  | 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) |