diff options
author | 2023-10-10 11:40:56 +0000 | |
---|---|---|
committer | 2023-10-10 11:40:56 +0000 | |
commit | e02cda008591317b1625707ff8e115a4841aa889 (patch) | |
tree | aee302e3cf8b59ec2d32ec481be3d1afddfc8968 /scripts/qapi/visit.py | |
parent | cc668e6b7e0ffd8c9d130513d12053cf5eda1d3b (diff) |
Introduce Virtio-loopback epsilon release:
Epsilon release introduces a new compatibility layer which make virtio-loopback
design to work with QEMU and rust-vmm vhost-user backend without require any
changes.
Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Change-Id: I52e57563e08a7d0bdc002f8e928ee61ba0c53dd9
Diffstat (limited to 'scripts/qapi/visit.py')
-rw-r--r-- | scripts/qapi/visit.py | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py new file mode 100644 index 000000000..e13bbe429 --- /dev/null +++ b/scripts/qapi/visit.py @@ -0,0 +1,410 @@ +""" +QAPI visitor generator + +Copyright IBM, Corp. 2011 +Copyright (C) 2014-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, + indent, + mcgen, +) +from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext +from .schema import ( + QAPISchema, + QAPISchemaEnumMember, + QAPISchemaEnumType, + QAPISchemaFeature, + QAPISchemaIfCond, + QAPISchemaObjectType, + QAPISchemaObjectTypeMember, + QAPISchemaType, + QAPISchemaVariants, +) +from .source import QAPISourceInfo + + +def gen_visit_decl(name: str, scalar: bool = False) -> str: + c_type = c_name(name) + ' *' + if not scalar: + c_type += '*' + return mcgen(''' + +bool visit_type_%(c_name)s(Visitor *v, const char *name, + %(c_type)sobj, Error **errp); +''', + c_name=c_name(name), c_type=c_type) + + +def gen_visit_members_decl(name: str) -> str: + return mcgen(''' + +bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); +''', + c_name=c_name(name)) + + +def gen_visit_object_members(name: str, + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> str: + ret = mcgen(''' + +bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) +{ +''', + c_name=c_name(name)) + + if base: + ret += mcgen(''' + if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) { + return false; + } +''', + c_type=base.c_name()) + + for memb in members: + ret += memb.ifcond.gen_if() + if memb.optional: + ret += mcgen(''' + if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { +''', + name=memb.name, c_name=c_name(memb.name)) + indent.increase() + special_features = gen_special_features(memb.features) + if special_features != '0': + ret += mcgen(''' + if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) { + return false; + } + if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) { +''', + name=memb.name, special_features=special_features) + indent.increase() + ret += mcgen(''' + if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) { + return false; + } +''', + c_type=memb.type.c_name(), name=memb.name, + c_name=c_name(memb.name)) + if special_features != '0': + indent.decrease() + ret += mcgen(''' + } +''') + if memb.optional: + indent.decrease() + ret += mcgen(''' + } +''') + ret += memb.ifcond.gen_endif() + + if variants: + tag_member = variants.tag_member + assert isinstance(tag_member.type, QAPISchemaEnumType) + + ret += mcgen(''' + switch (obj->%(c_name)s) { +''', + c_name=c_name(tag_member.name)) + + for var in variants.variants: + case_str = c_enum_const(tag_member.type.name, var.name, + tag_member.type.prefix) + ret += var.ifcond.gen_if() + if var.type.name == 'q_empty': + # valid variant and nothing to do + ret += mcgen(''' + case %(case)s: + break; +''', + case=case_str) + else: + ret += mcgen(''' + case %(case)s: + return visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, errp); +''', + case=case_str, + c_type=var.type.c_name(), c_name=c_name(var.name)) + + ret += var.ifcond.gen_endif() + ret += mcgen(''' + default: + abort(); + } +''') + + ret += mcgen(''' + return true; +} +''') + return ret + + +def gen_visit_list(name: str, element_type: QAPISchemaType) -> str: + return mcgen(''' + +bool visit_type_%(c_name)s(Visitor *v, const char *name, + %(c_name)s **obj, Error **errp) +{ + bool ok = false; + %(c_name)s *tail; + size_t size = sizeof(**obj); + + if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { + return false; + } + + for (tail = *obj; tail; + tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) { + if (!visit_type_%(c_elt_type)s(v, NULL, &tail->value, errp)) { + goto out_obj; + } + } + + ok = visit_check_list(v, errp); +out_obj: + visit_end_list(v, (void **)obj); + if (!ok && visit_is_input(v)) { + qapi_free_%(c_name)s(*obj); + *obj = NULL; + } + return ok; +} +''', + c_name=c_name(name), c_elt_type=element_type.c_name()) + + +def gen_visit_enum(name: str) -> str: + return mcgen(''' + +bool visit_type_%(c_name)s(Visitor *v, const char *name, + %(c_name)s *obj, Error **errp) +{ + int value = *obj; + bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp); + *obj = value; + return ok; +} +''', + c_name=c_name(name)) + + +def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str: + ret = mcgen(''' + +bool visit_type_%(c_name)s(Visitor *v, const char *name, + %(c_name)s **obj, Error **errp) +{ + bool ok = false; + + if (!visit_start_alternate(v, name, (GenericAlternate **)obj, + sizeof(**obj), errp)) { + return false; + } + if (!*obj) { + /* incomplete */ + assert(visit_is_dealloc(v)); + ok = true; + goto out_obj; + } + switch ((*obj)->type) { +''', + c_name=c_name(name)) + + for var in variants.variants: + ret += var.ifcond.gen_if() + ret += mcgen(''' + case %(case)s: +''', + case=var.type.alternate_qtype()) + if isinstance(var.type, QAPISchemaObjectType): + ret += mcgen(''' + if (!visit_start_struct(v, name, NULL, 0, errp)) { + break; + } + if (visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, errp)) { + ok = visit_check_struct(v, errp); + } + visit_end_struct(v, NULL); +''', + c_type=var.type.c_name(), + c_name=c_name(var.name)) + else: + ret += mcgen(''' + ok = visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, errp); +''', + c_type=var.type.c_name(), + c_name=c_name(var.name)) + ret += mcgen(''' + break; +''') + ret += var.ifcond.gen_endif() + + ret += mcgen(''' + case QTYPE_NONE: + abort(); + default: + assert(visit_is_input(v)); + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", + "%(name)s"); + /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */ + g_free(*obj); + *obj = NULL; + } +out_obj: + visit_end_alternate(v, (void **)obj); + if (!ok && visit_is_input(v)) { + qapi_free_%(c_name)s(*obj); + *obj = NULL; + } + return ok; +} +''', + name=name, c_name=c_name(name)) + + return ret + + +def gen_visit_object(name: str) -> str: + return mcgen(''' + +bool visit_type_%(c_name)s(Visitor *v, const char *name, + %(c_name)s **obj, Error **errp) +{ + bool ok = false; + + if (!visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), errp)) { + return false; + } + if (!*obj) { + /* incomplete */ + assert(visit_is_dealloc(v)); + ok = true; + goto out_obj; + } + if (!visit_type_%(c_name)s_members(v, *obj, errp)) { + goto out_obj; + } + ok = visit_check_struct(v, errp); +out_obj: + visit_end_struct(v, (void **)obj); + if (!ok && visit_is_input(v)) { + qapi_free_%(c_name)s(*obj); + *obj = NULL; + } + return ok; +} +''', + c_name=c_name(name)) + + +class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): + + def __init__(self, prefix: str): + super().__init__( + prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', + ' * Built-in QAPI visitors', __doc__) + + def _begin_builtin_module(self) -> None: + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-builtin-visit.h" +''')) + self._genh.preamble_add(mcgen(''' +#include "qapi/visitor.h" +#include "qapi/qapi-builtin-types.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/error.h" +#include "qapi/qmp/qerror.h" +#include "%(visit)s.h" +''', + visit=visit)) + self._genh.preamble_add(mcgen(''' +#include "qapi/qapi-builtin-visit.h" +#include "%(types)s.h" + +''', + types=types)) + + 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.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) + + 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.add(gen_visit_decl(name)) + self._genc.add(gen_visit_list(name, element_type)) + + 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._genc): + self._genh.add(gen_visit_members_decl(name)) + self._genc.add(gen_visit_object_members(name, base, + members, variants)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # only explicit types need an allocating visit + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_object(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._genc): + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_alternate(name, variants)) + + +def gen_visit(schema: QAPISchema, + output_dir: str, + prefix: str, + opt_builtins: bool) -> None: + vis = QAPISchemaGenVisitVisitor(prefix) + schema.visit(vis) + vis.write(output_dir, opt_builtins) |