From nobody Wed Sep 10 00:20:03 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1631019383419261.52861431568056; Tue, 7 Sep 2021 05:56:23 -0700 (PDT) Received: from localhost ([::1]:47910 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mNaeA-0006lZ-De for importer@patchew.org; Tue, 07 Sep 2021 08:56:18 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:42978) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mNa9S-0007Eq-0J for qemu-devel@nongnu.org; Tue, 07 Sep 2021 08:24:34 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:54668) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mNa9O-0008D2-5V for qemu-devel@nongnu.org; Tue, 07 Sep 2021 08:24:33 -0400 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-70-xzLggpJKNYGGXxHl2oUWyQ-1; Tue, 07 Sep 2021 08:24:22 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 041F7107ACCD for ; Tue, 7 Sep 2021 12:24:22 +0000 (UTC) Received: from localhost (unknown [10.39.208.23]) by smtp.corp.redhat.com (Postfix) with ESMTP id 225E710013D7; Tue, 7 Sep 2021 12:24:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1631017468; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=W3M1F8GEkUCk9wCGHM8RpT/NIDPmq2FRxLHZG7uAr5k=; b=a2Xu4BuPFGxdd/TrPegG8ZnWBhE8pVmybeAiUTv8ghczyJyGlKa5AXiRHSWE3obm8v59WM EgmdpcHydF9kEgrBWLjFpek8vw7vb2Q6jIj/eMe/FjTxPdiSqAJRm4pQIhg1iSE9ueeIub mTHKWW5xRq/iHWKkSE+fLwmEg8HG6ps= X-MC-Unique: xzLggpJKNYGGXxHl2oUWyQ-1 From: marcandre.lureau@redhat.com To: qemu-devel@nongnu.org Subject: [RFC v3 20/32] scripts/qapi: generate high-level Rust bindings Date: Tue, 7 Sep 2021 16:19:31 +0400 Message-Id: <20210907121943.3498701-21-marcandre.lureau@redhat.com> In-Reply-To: <20210907121943.3498701-1-marcandre.lureau@redhat.com> References: <20210907121943.3498701-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=marcandre.lureau@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=marcandre.lureau@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -31 X-Spam_score: -3.2 X-Spam_bar: --- X-Spam_report: (-3.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.391, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pbonzini@redhat.com, berrange@redhat.com, armbru@redhat.com, stefanha@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1631019384555100001 From: Marc-Andr=C3=A9 Lureau Generate high-level idiomatic Rust code for the QAPI types, with to/from translations for the C FFI. - char* is mapped to String, scalars to there corresponding Rust types - enums are simply aliased from FFI - has_foo/foo members are mapped to Option - lists are represented as Vec - structures have Rust versions, with To/From FFI conversions - alternate are represented as Rust enum - unions are represented in a similar way as in C: a struct S with a "u" member (since S may have extra 'base' fields). However, the discriminant isn't a member of S, since Rust enum already include it. Signed-off-by: Marc-Andr=C3=A9 Lureau --- meson.build | 1 + scripts/qapi/main.py | 2 + scripts/qapi/rs.py | 94 +++- scripts/qapi/rs_types.py | 966 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 1062 insertions(+), 1 deletion(-) create mode 100644 scripts/qapi/rs_types.py diff --git a/meson.build b/meson.build index 74e90059c2..8e12a4dd70 100644 --- a/meson.build +++ b/meson.build @@ -2017,6 +2017,7 @@ qapi_gen_depends =3D [ meson.source_root() / 'scripts= /qapi/__init__.py', meson.source_root() / 'scripts/qapi/common.py', meson.source_root() / 'scripts/qapi/rs.py', meson.source_root() / 'scripts/qapi/rs_ffi.py', + meson.source_root() / 'scripts/qapi/rs_types.py', meson.source_root() / 'scripts/qapi-gen.py', ] =20 diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index deba72ee4e..9756c0c35d 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -17,6 +17,7 @@ from .events import gen_events from .introspect import gen_introspect from .rs_ffi import gen_rs_ffitypes +from .rs_types import gen_rs_types from .schema import QAPISchema from .types import gen_types from .visit import gen_visit @@ -52,6 +53,7 @@ def generate(schema_file: str, schema =3D QAPISchema(schema_file) if rust: gen_rs_ffitypes(schema, output_dir, prefix) + gen_rs_types(schema, output_dir, prefix) else: gen_types(schema, output_dir, prefix, builtins) gen_visit(schema, output_dir, prefix, builtins) diff --git a/scripts/qapi/rs.py b/scripts/qapi/rs.py index be42329fa4..b53930eab2 100644 --- a/scripts/qapi/rs.py +++ b/scripts/qapi/rs.py @@ -9,7 +9,7 @@ import subprocess from typing import NamedTuple, Optional =20 -from .common import POINTER_SUFFIX +from .common import POINTER_SUFFIX, mcgen from .gen import QAPIGen from .schema import QAPISchemaModule, QAPISchemaVisitor =20 @@ -53,6 +53,64 @@ def rs_name(name: str, protect: bool =3D True) -> str: return name =20 =20 +def rs_type(c_type: str, + qapi_ns: Optional[str] =3D 'qapi::', + optional: Optional[bool] =3D False, + box: bool =3D False) -> str: + (is_pointer, _, is_list, c_type) =3D rs_ctype_parse(c_type) + # accepts QAPI types ('any', 'str', ...) as we translate + # qapiList to Rust FFI types here. + to_rs =3D { + 'any': 'QObject', + 'bool': 'bool', + 'char': 'i8', + 'double': 'f64', + 'int': 'i64', + 'int16': 'i16', + 'int16_t': 'i16', + 'int32': 'i32', + 'int32_t': 'i32', + 'int64': 'i64', + 'int64_t': 'i64', + 'int8': 'i8', + 'int8_t': 'i8', + 'null': 'QNull', + 'number': 'f64', + 'size': 'u64', + 'str': 'String', + 'uint16': 'u16', + 'uint16_t': 'u16', + 'uint32': 'u32', + 'uint32_t': 'u32', + 'uint64': 'u64', + 'uint64_t': 'u64', + 'uint8': 'u8', + 'uint8_t': 'u8', + 'String': 'QapiString', + } + if is_pointer: + to_rs.update({ + 'char': 'String', + }) + + if is_list: + c_type =3D c_type[:-4] + + to_rs =3D to_rs.get(c_type) + if to_rs: + ret =3D to_rs + else: + ret =3D qapi_ns + c_type + + if is_list: + ret =3D 'Vec<%s>' % ret + elif is_pointer and not to_rs and box: + ret =3D 'Box<%s>' % ret + if optional: + ret =3D 'Option<%s>' % ret + return ret + + class CType(NamedTuple): is_pointer: bool is_const: bool @@ -140,6 +198,40 @@ def to_snake_case(value: str) -> str: return snake_case.sub(r'_\1', value).lower() =20 =20 +def to_qemu_none(c_type: str, name: str) -> str: + (is_pointer, _, is_list, _) =3D rs_ctype_parse(c_type) + + if is_pointer: + if c_type =3D=3D 'char': + return mcgen(''' + let %(name)s_ =3D CString::new(%(name)s).unwrap(); + let %(name)s =3D %(name)s_.as_ptr(); +''', name=3Dname) + if is_list: + return mcgen(''' + let %(name)s_ =3D NewPtr(%(name)s).to_qemu_none(); + let %(name)s =3D %(name)s_.0.0; +''', name=3Dname) + return mcgen(''' + let %(name)s_ =3D %(name)s.to_qemu_none(); + let %(name)s =3D %(name)s_.0; +''', name=3Dname) + return '' + + +def from_qemu(var_name: str, c_type: str, full: Optional[bool] =3D False) = -> str: + (is_pointer, _, is_list, c_type) =3D rs_ctype_parse(c_type) + ptr =3D '{} as *{} _'.format(var_name, 'mut' if full else 'const') + if is_list: + ptr =3D 'NewPtr({})'.format(ptr) + if is_pointer: + ret =3D 'from_qemu_{}({})'.format('full' if full else 'none', ptr) + if c_type !=3D 'char' and not is_list: + ret =3D 'Box::new(%s)' % ret + return ret + return var_name + + class QAPIGenRs(QAPIGen): pass =20 diff --git a/scripts/qapi/rs_types.py b/scripts/qapi/rs_types.py new file mode 100644 index 0000000000..eb9877a0de --- /dev/null +++ b/scripts/qapi/rs_types.py @@ -0,0 +1,966 @@ +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. +""" +QAPI Rust types generator +""" + +from typing import List, Optional + +from .common import POINTER_SUFFIX, mcgen +from .rs import ( + QAPISchemaRsVisitor, + from_qemu, + rs_ctype_parse, + rs_ffitype, + rs_name, + rs_type, + to_camel_case, + to_snake_case, +) +from .schema import ( + QAPISchema, + QAPISchemaEnumMember, + QAPISchemaEnumType, + QAPISchemaFeature, + QAPISchemaIfCond, + QAPISchemaObjectType, + QAPISchemaObjectTypeMember, + QAPISchemaType, + QAPISchemaVariants, +) +from .source import QAPISourceInfo + + +objects_seen =3D set() + + +def gen_rs_variants_to_tag(name: str, + ifcond: QAPISchemaIfCond, + variants: Optional[QAPISchemaVariants]) -> str: + ret =3D mcgen(''' + +%(cfg)s +impl From<&%(rs_name)sVariant> for %(tag)s { + fn from(e: &%(rs_name)sVariant) -> Self { + match e { + ''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + tag=3Drs_type(variants.tag_member.type.c_type(), '')) + + for var in variants.variants: + type_name =3D var.type.name + var_name =3D to_camel_case(rs_name(var.name)) + patt =3D '(_)' + if type_name =3D=3D 'q_empty': + patt =3D '' + ret +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(var_name)s%(patt)s =3D> Self::%(var_name)s, +''', + cfg=3Dvar.ifcond.rsgen(), + rs_name=3Drs_name(name), + var_name=3Dvar_name, + patt=3Dpatt) + + ret +=3D mcgen(''' + } + } +} +''') + return ret + + +def variants_to_qemu_inner(name: str, + variants: Optional[QAPISchemaVariants]) -> str: + members =3D '' + none_arms =3D '' + full_arms =3D '' + lifetime =3D '' + for var in variants.variants: + var_name =3D to_camel_case(rs_name(var.name)) + type_name =3D var.type.name + if type_name =3D=3D 'q_empty': + members +=3D mcgen(''' + %(cfg)s + %(var_name)s, +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name) + none_arms +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(var_name)s =3D> { + (std::ptr::null_mut(), + %(rs_name)sVariantStorage::%(var_name)s) + }, +''', + cfg=3Dvar.ifcond.rsgen(), + rs_name=3Drs_name(name), + var_name=3Dvar_name) + full_arms +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(var_name)s =3D> { + std::ptr::null_mut() + } +''', + cfg=3Dvar.ifcond.rsgen(), + rs_name=3Drs_name(name), + var_name=3Dvar_name) + continue + c_type =3D var.type.c_unboxed_type() + if type_name.endswith('-wrapper'): + wrap =3D list(var.type.members)[0] + type_name =3D wrap.type.name + c_type =3D wrap.type.c_unboxed_type() + + lifetime =3D "<'a>" + (_, _, is_list, ffitype) =3D rs_ctype_parse(c_type) + ffitype =3D rs_ffitype(ffitype) + ptr_ty =3D 'NewPtr<*mut %s>' % ffitype if is_list else '*mut ' + f= fitype + stash_ty =3D ': Stash<%s, _>' % ptr_ty if is_list else '' + + members +=3D mcgen(''' + %(cfg)s + %(var_name)s(<%(rs_type)s as ToQemuPtr<'a, %(ptr_ty)s>>::Storage), +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name, + rs_type=3Drs_type(c_type, ''), + ptr_ty=3Dptr_ty) + none_arms +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(var_name)s(v) =3D> { + let stash_%(stash_ty)s =3D v.to_qemu_none(); + (stash_.0.to() as *mut std::ffi::c_void, + %(rs_name)sVariantStorage::%(var_name)s(stash_.1)) + }, +''', + cfg=3Dvar.ifcond.rsgen(), + rs_name=3Drs_name(name), + var_name=3Dvar_name, + stash_ty=3Dstash_ty) + ptr_ty =3D ': %s' % ptr_ty if is_list else '' + full_arms +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(var_name)s(v) =3D> { + let ptr%(ptr_ty)s =3D v.to_qemu_full(); + ptr.to() as *mut std::ffi::c_void + }, +''', + cfg=3Dvar.ifcond.rsgen(), + rs_name=3Drs_name(name), + var_name=3Dvar_name, + ptr_ty=3Dptr_ty) + return (members, none_arms, full_arms, lifetime) + + +def gen_rs_variants_to_qemu(name: str, + ifcond: QAPISchemaIfCond, + variants: Optional[QAPISchemaVariants]) -> str: + (members, none_arms, full_arms, lifetime) =3D \ + variants_to_qemu_inner(name, variants) + return mcgen(''' + +%(cfg)s +impl QemuPtrDefault for %(rs_name)sVariant { + type QemuType =3D *mut std::ffi::c_void; +} + +%(cfg)s +pub enum %(rs_name)sVariantStorage%(lt)s { + %(members)s +} + +%(cfg)s +impl<'a> ToQemuPtr<'a, *mut std::ffi::c_void> for %(rs_name)sVariant { + type Storage =3D %(rs_name)sVariantStorage%(lt)s; + + #[inline] + fn to_qemu_none(&'a self) + -> Stash<'a, *mut std::ffi::c_void, %(rs_name)sVariant> { + let (ptr_, cenum_) =3D match self { + %(none_arms)s + }; + + Stash(ptr_, cenum_) + } + + #[inline] + fn to_qemu_full(&self) -> *mut std::ffi::c_void { + match self { + %(full_arms)s + } + } +} +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + lt=3Dlifetime, + members=3Dmembers, + none_arms=3Dnone_arms, + full_arms=3Dfull_arms) + + +def gen_rs_variants(name: str, + ifcond: QAPISchemaIfCond, + variants: Optional[QAPISchemaVariants]) -> str: + ret =3D mcgen(''' + +%(cfg)s +#[derive(Clone,Debug)] +pub enum %(rs_name)sVariant { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + for var in variants.variants: + type_name =3D var.type.name + var_name =3D to_camel_case(rs_name(var.name, False)) + if type_name =3D=3D 'q_empty': + ret +=3D mcgen(''' + %(cfg)s + %(var_name)s, +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name) + else: + c_type =3D var.type.c_unboxed_type() + if c_type.endswith('_wrapper'): + c_type =3D c_type[6:-8] # remove q_obj*-wrapper + ret +=3D mcgen(''' + %(cfg)s + %(var_name)s(%(rs_type)s), +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name, + rs_type=3Drs_type(c_type, '')) + + ret +=3D mcgen(''' +} +''') + + ret +=3D gen_rs_variants_to_tag(name, ifcond, variants) + # implement ToQemu trait for the storage handling + # no need for gen_rs_variants_from_qemu() however + ret +=3D gen_rs_variants_to_qemu(name, ifcond, variants) + + return ret + + +def gen_rs_object_to_qemu(name: str, + ifcond: QAPISchemaIfCond, + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> str: + storage =3D [] + stash =3D [] + ffi_memb =3D [] + memb_none =3D '' + memb_full =3D '' + if base: + members =3D list(base.members) + members + for memb in members: + if variants and variants.tag_member.name =3D=3D memb.name: + continue + memb_name =3D to_snake_case(rs_name(memb.name)) + c_type =3D memb.type.c_type() + (is_pointer, _, is_list, _) =3D rs_ctype_parse(c_type) + if is_pointer: + if memb.ifcond.is_present(): + raise NotImplementedError("missing support for condition h= ere") + typ =3D rs_type(memb.type.c_type(), + optional=3Dmemb.optional, + qapi_ns=3D'', + box=3DTrue) + styp =3D rs_ffitype(memb.type.c_type(), list_as_newp=3DTrue) + storage.append("Stash<'a, %s, %s>" % (styp, typ)) + if memb.optional: + has_memb_name =3D 'has_%s' % rs_name(memb.name, protect=3DFals= e) + ffi_memb.append(f"{memb.ifcond.rsgen()} {has_memb_name}") + has_memb =3D mcgen(''' + %(cfg)s + let %(has_memb_name)s =3D self.%(memb_name)s.is_some(); +''', + cfg=3Dmemb.ifcond.rsgen(), + memb_name=3Dmemb_name, + has_memb_name=3Dhas_memb_name) + memb_none +=3D has_memb + memb_full +=3D has_memb + + if is_pointer: + stash_name =3D '{}_stash_'.format(memb_name) + stash.append(stash_name) + var =3D 'NewPtr(%s)' % memb_name if is_list else memb_name + memb_none +=3D mcgen(''' + let %(stash_name)s =3D self.%(memb_name)s.to_qemu_none(); + let %(var)s =3D %(stash_name)s.0; +''', stash_name=3Dstash_name, memb_name=3Dmemb_name, var=3Dvar) + memb_full +=3D mcgen(''' + let %(var)s =3D self.%(memb_name)s.to_qemu_full(); +''', memb_name=3Dmemb_name, var=3Dvar) + else: + unwrap =3D '' + if memb.optional: + unwrap =3D '.unwrap_or_default()' + assign =3D mcgen(''' + %(cfg)s + let %(memb_name)s =3D self.%(memb_name)s%(unwrap)s; +''', + cfg=3Dmemb.ifcond.rsgen(), + memb_name=3Dmemb_name, + unwrap=3Dunwrap) + memb_none +=3D assign + memb_full +=3D assign + + ffi_memb.append(f"{memb.ifcond.rsgen()} {memb_name}") + + if variants: + tag_name =3D rs_name(variants.tag_member.name) + ffi_memb.append(tag_name) + ffi_memb.append('u') + voidp =3D '*mut std::ffi::c_void' + storage.append("Stash<'a, %s, %sVariant>" % (voidp, rs_name(name))) + tag =3D mcgen(''' + let %(tag_name)s =3D (&self.u).into(); +''', tag_name=3Dtag_name) + memb_none +=3D tag + memb_full +=3D tag + arms_none =3D '' + arms_full =3D '' + for variant in variants.variants: + typ =3D variant.type + if typ.name =3D=3D 'q_empty': + arms_none +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariantStorage::%(kind)s =3D> qapi_ffi::%(rs_name)sUnion { + qapi_dummy: qapi_ffi::QapiDummy, + },''', + cfg=3Dvariant.ifcond.rsgen(), + rs_name=3Drs_name(name), + kind=3Dto_camel_case(rs_name(variant.na= me))) + arms_full +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(kind)s =3D> qapi_ffi::%(rs_name)sUnion { + qapi_dummy: qapi_ffi::QapiDummy, + },''', + cfg=3Dvariant.ifcond.rsgen(), + rs_name=3Drs_name(name), + kind=3Dto_camel_case(rs_name(variant.na= me))) + else: + if typ.name.endswith('-wrapper'): + wrap_ty =3D list(typ.members)[0].type.c_type() + ptr =3D wrap_ty.endswith(POINTER_SUFFIX) + val =3D ( + rs_ffitype(variant.type.c_unboxed_type()) + + ' { data: u_stash_.0.to() as *mut _ }' if ptr else + ' { data: unsafe { *(u_stash_.0.to() as *const _) = } }' + ) + else: + val =3D '*_s.0' + arms_none +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariantStorage::%(kind)s(ref _s) =3D> qapi_ffi::%(rs_name)s= Union { + %(var_name)s: %(val)s, + },''', + cfg=3Dvariant.ifcond.rsgen(), + rs_name=3Drs_name(name), + kind=3Dto_camel_case(rs_name(variant.na= me)), + var_name=3Drs_name(variant.name), + val=3Dval) + arms_full +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(kind)s(_) =3D> qapi_ffi::%(rs_name)sUnion { + %(var_name)s: *(u_ptr_.to() as *const _), + },''', + cfg=3Dvariant.ifcond.rsgen(), + rs_name=3Drs_name(name), + kind=3Dto_camel_case(rs_name(variant.na= me)), + var_name=3Drs_name(variant.name)) + memb_none +=3D mcgen(''' + let u_stash_ =3D self.u.to_qemu_none(); + let u =3D match u_stash_.1 { + %(arms)s + }; +''', arms=3Darms_none) + stash.append('u_stash_') + memb_full +=3D mcgen(''' + let u_ptr_ =3D self.u.to_qemu_full(); + let u =3D match self.u { + %(arms)s + }; + ffi::g_free(u_ptr_); +''', arms=3Darms_full) + + if not ffi_memb: + ffi_memb =3D ['qapi_dummy_for_empty_struct: 0'] + + return mcgen(''' + +%(cfg)s +impl QemuPtrDefault for %(rs_name)s { + type QemuType =3D *mut qapi_ffi::%(rs_name)s; +} + +%(cfg)s +impl<'a> ToQemuPtr<'a, *mut qapi_ffi::%(rs_name)s> for %(rs_name)s { + type Storage =3D (Box, %(storage)s); + + #[inline] + fn to_qemu_none(&'a self) + -> Stash<'a, *mut qapi_ffi::%(rs_name)s, %(rs_name)s> { + %(memb_none)s + let mut box_ =3D Box::new(qapi_ffi::%(rs_name)s { %(ffi_memb)s }); + + Stash(&mut *box_, (box_, %(stash)s)) + } + + #[inline] + fn to_qemu_full(&self) -> *mut qapi_ffi::%(rs_name)s { + unsafe { + %(memb_full)s + let ptr =3D ffi::g_malloc0( + std::mem::size_of::<%(rs_name)s>()) as *mut _; + *ptr =3D qapi_ffi::%(rs_name)s { %(ffi_memb)s }; + ptr + } + } +} +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + storage=3D', '.join(storage), + ffi_memb=3D', '.join(ffi_memb), + memb_none=3Dmemb_none, + memb_full=3Dmemb_full, + stash=3D', '.join(stash)) + + +def gen_rs_members(members: List[QAPISchemaObjectTypeMember], + exclude: List[str] =3D None) -> str: + exclude =3D exclude or [] + return [f"{m.ifcond.rsgen()} {to_snake_case(rs_name(m.name))}" + for m in members if m.name not in exclude] + + +def gen_rs_object_from_qemu(name: str, + ifcond: QAPISchemaIfCond, + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> str: + exclude =3D [variants.tag_member.name] if variants else [] + memb_names =3D [] + if base: + names =3D gen_rs_members(base.members, exclude) + memb_names.extend(names) + names =3D gen_rs_members(members, exclude) + memb_names.extend(names) + + ret =3D mcgen(''' + +%(cfg)s +impl FromQemuPtrFull<*mut qapi_ffi::%(rs_name)s> for %(rs_name)s { + unsafe fn from_qemu_full(ffi: *mut qapi_ffi::%(rs_name)s) -> Self { + let ret =3D from_qemu_none(ffi as *const _); + qapi_ffi::qapi_free_%(name)s(ffi); + ret + } +} + +%(cfg)s +impl FromQemuPtrNone<*const qapi_ffi::%(rs_name)s> for %(rs_name)s { + unsafe fn from_qemu_none(ffi: *const qapi_ffi::%(rs_name)s) -> Self { + let _ffi =3D &*ffi; +''', + cfg=3Difcond.rsgen(), + name=3Drs_name(name, protect=3DFalse), + rs_name=3Drs_name(name)) + + if base: + members =3D list(base.members) + members + + tag_member =3D variants.tag_member if variants else None + for memb in members: + if memb =3D=3D tag_member: + continue + memb_name =3D rs_name(memb.name) + val =3D from_qemu('_ffi.' + to_snake_case(memb_name), memb.type.c_= type()) + if memb.optional: + val =3D mcgen('''{ + if _ffi.has_%(memb_name)s { + Some(%(val)s) + } else { + None + } +}''', + memb_name=3Drs_name(memb.name, protect=3DFalse), + val=3Dval) + + ret +=3D mcgen(''' + %(cfg)s + let %(snake_memb_name)s =3D %(val)s; +''', + cfg=3Dmemb.ifcond.rsgen(), + snake_memb_name=3Dto_snake_case(memb_name), + memb_name=3Dmemb_name, + val=3Dval) + + if variants: + arms =3D '' + assert isinstance(variants.tag_member.type, QAPISchemaEnumType) + for variant in variants.variants: + typ =3D variant.type + if typ.name =3D=3D 'q_empty': + memb =3D '' + else: + ptr =3D True + is_list =3D False + memb =3D to_snake_case(rs_name(variant.name)) + if typ.name.endswith('-wrapper'): + memb =3D '_ffi.u.%s.data' % memb + wrap_ty =3D list(typ.members)[0].type.c_type() + (ptr, _, is_list, _) =3D rs_ctype_parse(wrap_ty) + else: + memb =3D '&_ffi.u.%s' % memb + if ptr: + memb =3D '%s as *const _' % memb + if is_list: + memb =3D 'NewPtr(%s)' % memb + memb =3D 'from_qemu_none(%s)' % memb + memb =3D '(%s)' % memb + arms +=3D mcgen(''' +%(cfg)s +%(enum)s::%(variant)s =3D> { %(rs_name)sVariant::%(variant)s%(memb)s }, +''', + cfg=3Dvariant.ifcond.rsgen(), + enum=3Drs_name(variants.tag_member.type.name), + memb=3Dmemb, + variant=3Dto_camel_case(rs_name(variant.name)), + rs_name=3Drs_name(name)) + ret +=3D mcgen(''' + let u =3D match _ffi.%(tag)s { + %(arms)s + _ =3D> panic!("Variant with invalid tag"), + }; +''', + tag=3Drs_name(variants.tag_member.name), + arms=3Darms) + memb_names.append('u') + + ret +=3D mcgen(''' + Self { %(memb_names)s } + } +} +''', + memb_names=3D', '.join(memb_names)) + return ret + + +def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: + ret =3D '' + for memb in members: + typ =3D rs_type(memb.type.c_type(), '', optional=3Dmemb.optional, = box=3DTrue) + ret +=3D mcgen(''' + %(cfg)s + pub %(rs_name)s: %(rs_type)s, +''', + cfg=3Dmemb.ifcond.rsgen(), + rs_type=3Dtyp, + rs_name=3Dto_snake_case(rs_name(memb.name))) + return ret + + +def gen_rs_object(name: str, + ifcond: QAPISchemaIfCond, + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> str: + if name in objects_seen: + return '' + + if variants: + members =3D [m for m in members + if m.name !=3D variants.tag_member.name] + + ret =3D '' + objects_seen.add(name) + + if variants: + ret +=3D gen_rs_variants(name, ifcond, variants) + + ret +=3D mcgen(''' + +%(cfg)s +#[derive(Clone, Debug)] +pub struct %(rs_name)s { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + if base: + if not base.is_implicit(): + ret +=3D mcgen(''' + // Members inherited: +''', + c_name=3Dbase.c_name()) + base_members =3D base.members + if variants: + base_members =3D [m for m in base.members + if m.name !=3D variants.tag_member.name] + ret +=3D gen_struct_members(base_members) + if not base.is_implicit(): + ret +=3D mcgen(''' + // Own members: +''') + + ret +=3D gen_struct_members(members) + + if variants: + ret +=3D mcgen(''' + pub u: %(rs_type)sVariant, +''', rs_type=3Drs_name(name)) + ret +=3D mcgen(''' +} +''') + + ret +=3D gen_rs_object_from_qemu(name, ifcond, base, members, variants) + ret +=3D gen_rs_object_to_qemu(name, ifcond, base, members, variants) + return ret + + +def gen_rs_alternate_from_qemu(name: str, + ifcond: QAPISchemaIfCond, + variants: Optional[QAPISchemaVariants]) -> = str: + arms =3D '' + for var in variants.variants: + qtype =3D to_camel_case(var.type.alternate_qtype()[6:].lower()) + ptr =3D var.type.c_unboxed_type().endswith(POINTER_SUFFIX) + memb =3D 'ffi.u.%s' % rs_name(var.name) + if not ptr: + memb =3D '&' + memb + arms +=3D mcgen(''' + %(cfg)s + QType::%(qtype)s =3D> { + Self::%(kind)s(from_qemu_none(%(memb)s as *const _)) + } +''', + qtype=3Dqtype, + cfg=3Dvar.ifcond.rsgen(), + kind=3Dto_camel_case(rs_name(var.name)), + memb=3Dmemb) + + ret =3D mcgen(''' + +%(cfg)s +impl FromQemuPtrFull<*mut qapi_ffi::%(rs_name)s> for %(rs_name)s { + unsafe fn from_qemu_full(ffi: *mut qapi_ffi::%(rs_name)s) -> Self { + let ret =3D from_qemu_none(ffi as *const _); + qapi_ffi::qapi_free_%(name)s(ffi); + ret + } +} + +%(cfg)s +impl FromQemuPtrNone<*const qapi_ffi::%(rs_name)s> for %(rs_name)s { + unsafe fn from_qemu_none(ffi: *const qapi_ffi::%(rs_name)s) -> Self { + let ffi =3D &*ffi; + + match ffi.r#type { + %(arms)s + _ =3D> panic!() + } + } +} +''', + cfg=3Difcond.rsgen(), + name=3Drs_name(name, protect=3DFalse), + rs_name=3Drs_name(name), + arms=3Darms) + return ret + + +def gen_rs_alternate_to_qemu(name: str, + ifcond: QAPISchemaIfCond, + variants: Optional[QAPISchemaVariants], + lifetime: str) -> str: + arms_none =3D '' + arms_full =3D '' + for var in variants.variants: + if var.type.name =3D=3D 'q_empty': + continue + ptr =3D var.type.c_unboxed_type().endswith(POINTER_SUFFIX) + val =3D 'val.0' if ptr else 'unsafe { *(val.0.to() as *const _) }' + stor =3D '(val.1)' if var.type.c_type().endswith(POINTER_SUFFIX) e= lse '' + qtype =3D var.type.alternate_qtype()[6:].lower() + arms_none +=3D mcgen(''' + %(cfg)s + Self::%(memb_name)s(val) =3D> { + let val =3D val.to_qemu_none(); + ( + QType::%(qtype)s, + qapi_ffi::%(rs_name)sUnion { %(ffi_memb_name)s: %(val)s }, + %(rs_name)sStorage::%(memb_name)s%(stor)s + ) + } +''', + rs_name=3Drs_name(name), + cfg=3Dvar.ifcond.rsgen(), + memb_name=3Dto_camel_case(rs_name(var.name)), + ffi_memb_name=3Drs_name(var.name), + qtype=3Dto_camel_case(qtype), + val=3Dval, + stor=3Dstor) + val =3D 'val' if ptr else '*val' + free =3D '' if ptr else 'ffi::g_free(val as *mut _);' + arms_full +=3D mcgen(''' + %(cfg)s + Self::%(memb_name)s(val) =3D> { + let val =3D val.to_qemu_full(); + let ret =3D (QType::%(qtype)s, qapi_ffi::%(rs_name)sUnion { + %(ffi_memb_name)s: %(val)s + } ); + %(free)s + ret + } +''', + rs_name=3Drs_name(name), + cfg=3Dvar.ifcond.rsgen(), + memb_name=3Dto_camel_case(rs_name(var.name)), + ffi_memb_name=3Drs_name(var.name), + qtype=3Dto_camel_case(qtype), + val=3Dval, + free=3Dfree) + + memb_none =3D mcgen(''' + let (r#type, u, stor) =3D match self { + %(arms_none)s + }; +''', arms_none=3Darms_none) + memb_full =3D mcgen(''' + let (r#type, u) =3D match self { + %(arms_full)s + }; +''', arms_full=3Darms_full) + ffi_memb =3D ['r#type', 'u'] + return mcgen(''' + +%(cfg)s +impl QemuPtrDefault for %(rs_name)s { + type QemuType =3D *mut qapi_ffi::%(rs_name)s; +} + +%(cfg)s +impl<'a> ToQemuPtr<'a, *mut qapi_ffi::%(rs_name)s> for %(rs_name)s { + // Additional boxing of storage needed due to recursive types + type Storage =3D (Box, Box<%(rs_name)sStorage%(= lt)s>); + + #[inline] + fn to_qemu_none(&'a self) + -> Stash<'a, *mut qapi_ffi::%(rs_name)s, %(rs_name)s> { + %(memb_none)s + let mut box_ =3D Box::new(qapi_ffi::%(rs_name)s { %(ffi_memb)s }); + + Stash(&mut *box_, (box_, Box::new(stor))) + } + + #[inline] + fn to_qemu_full(&self) -> *mut qapi_ffi::%(rs_name)s { + unsafe { + %(memb_full)s + let ptr =3D ffi::g_malloc0( + std::mem::size_of::<%(rs_name)s>()) as *mut _; + *ptr =3D qapi_ffi::%(rs_name)s { %(ffi_memb)s }; + ptr + } + } +} +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + lt=3Dlifetime, + ffi_memb=3D', '.join(ffi_memb), + memb_none=3Dmemb_none, + memb_full=3Dmemb_full) + + +def gen_rs_alternate(name: str, + ifcond: QAPISchemaIfCond, + variants: Optional[QAPISchemaVariants]) -> str: + if name in objects_seen: + return '' + + ret =3D '' + objects_seen.add(name) + + ret +=3D mcgen(''' +%(cfg)s +#[derive(Clone, Debug)] +pub enum %(rs_name)s { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + for var in variants.variants: + if var.type.name =3D=3D 'q_empty': + continue + ret +=3D mcgen(''' + %(cfg)s + %(mem_name)s(%(rs_type)s), +''', + cfg=3Dvar.ifcond.rsgen(), + rs_type=3Drs_type(var.type.c_unboxed_type(), ''), + mem_name=3Dto_camel_case(rs_name(var.name))) + + membs =3D '' + lifetime =3D '' + for var in variants.variants: + var_name =3D to_camel_case(rs_name(var.name)) + type_name =3D var.type.name + if type_name =3D=3D 'q_empty': + continue + if not var.type.c_type().endswith(POINTER_SUFFIX): + membs +=3D mcgen(''' + %(cfg)s + %(var_name)s, +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name) + else: + lifetime =3D "<'a>" + c_type =3D var.type.c_type() + ptr_ty =3D rs_ffitype(c_type) + membs +=3D mcgen(''' + %(cfg)s + %(var_name)s(<%(rs_type)s as ToQemuPtr<'a, %(ptr_ty)s>>::Storage), +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name, + rs_type=3Drs_type(c_type, ''), + ptr_ty=3Dptr_ty) + ret +=3D mcgen(''' +} + +%(cfg)s +pub enum %(rs_name)sStorage%(lt)s { + %(membs)s +} +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + lt=3Dlifetime, + membs=3Dmembs) + + ret +=3D gen_rs_alternate_from_qemu(name, ifcond, variants) + ret +=3D gen_rs_alternate_to_qemu(name, ifcond, variants, lifetime) + + return ret + + +def gen_rs_enum(name: str, ifcond: QAPISchemaIfCond) -> str: + return mcgen(''' + +%(cfg)s +pub type %(rs_name)s =3D qapi_ffi::%(ffi_name)s; + +%(cfg)s +impl_to_qemu_scalar_boxed!(%(rs_name)s); + +%(cfg)s +impl_from_qemu_none_scalar!(%(rs_name)s); +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + ffi_name=3Drs_name(name)) + + +class QAPISchemaGenRsTypeVisitor(QAPISchemaRsVisitor): + + def __init__(self, prefix: str) -> None: + super().__init__(prefix, 'qapi-types') + + def visit_begin(self, schema: QAPISchema) -> None: + # don't visit the empty type + objects_seen.add(schema.the_empty_object_type.name) + self._gen.preamble_add( + mcgen(''' +// generated by qapi-gen, DO NOT EDIT + +use common::{QNull, QObject}; +use crate::qapi_ffi; + +''')) + + def visit_array_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + element_type: QAPISchemaType) -> None: + typ =3D rs_type(name, qapi_ns=3D'') + scalar =3D False + if name[:-4] in {'number', + 'int', + 'int8', + 'int16', + 'int32', + 'int64', + 'uint8', + 'uint16', + 'uint32', + 'uint64', + 'size', + 'bool'}: + scalar =3D True + if isinstance(element_type, QAPISchemaEnumType): + scalar =3D True + + self._gen.add(mcgen(''' +%(cfg)s +mod %(mod)s_module { + use super::*; + + vec_type!(%(rs)s, %(ffi)s, qapi_free_%(name)s, %(scalar)i); +} + +%(cfg)s +pub use %(mod)s_module::*; +''', + cfg=3Difcond.rsgen(), + name=3Drs_name(name, protect=3DFalse), + mod=3Drs_name(name).lower(), + ffi=3Drs_name(name), + rs=3Dtyp, + scalar=3Dscalar)) + + 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: + if name.startswith('q_'): + return + self._gen.add(gen_rs_object(name, ifcond, base, members, variants)) + + def visit_enum_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str]) -> None: + self._gen.add(gen_rs_enum(name, ifcond)) + + def visit_alternate_type(self, + name: str, + info: QAPISourceInfo, + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + variants: QAPISchemaVariants) -> None: + self._gen.add(gen_rs_alternate(name, ifcond, variants)) + + +def gen_rs_types(schema: QAPISchema, output_dir: str, prefix: str) -> None: + vis =3D QAPISchemaGenRsTypeVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) --=20 2.33.0.113.g6c40894d24