slint/helper_crates/vtable/tests/test_vtable.rs
Olivier Goffart f39b3ab888 Use "C-unwind" API in our vtable
When using rust, allow panics to cross the boundaries of our vtable traits.
This avoids panic producing two backtrace with panic=unwind

This patch doesn't touch the ABI of out FFI interface, they stay extern "C", because
if a panic or exception crosses these boundaries, we are in trouble.
(Also, we have a panic=abort in our Cargo.toml anyway)
2025-06-06 14:47:46 +02:00

206 lines
5.3 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT OR Apache-2.0
#![no_std]
extern crate alloc;
use crate::alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::string::String;
use core::pin::Pin;
use vtable::*;
#[vtable]
/// This is the actual doc
struct HelloVTable {
foo: fn(VRef<'_, HelloVTable>, u32) -> u32,
foo_mut: extern "C" fn(VRefMut<'_, HelloVTable>, u32) -> u32,
construct: fn(*const HelloVTable, u32) -> VBox<HelloVTable>,
assoc: fn(*const HelloVTable) -> isize,
with_lifetime: extern "C-unwind" fn(VRef<'_, HelloVTable>) -> &'_ u32,
drop: fn(VRefMut<'_, HelloVTable>),
CONSTANT: usize,
#[field_offset(u32)]
SOME_OFFSET: usize,
}
#[derive(Debug, const_field_offset::FieldOffsets)]
#[repr(C)]
struct SomeStruct {
e: u8,
x: u32,
}
impl Hello for SomeStruct {
fn foo(&self, xx: u32) -> u32 {
self.x + xx
}
fn foo_mut(&mut self, xx: u32) -> u32 {
self.x += xx;
self.x
}
fn construct(init: u32) -> Self {
Self { e: 3, x: init }
}
fn assoc() -> isize {
32
}
fn with_lifetime(&self) -> &u32 {
&self.x
}
}
impl HelloConsts for SomeStruct {
const CONSTANT: usize = 88;
const SOME_OFFSET: const_field_offset::FieldOffset<SomeStruct, u32> =
SomeStruct::FIELD_OFFSETS.x;
}
HelloVTable_static!(static SOME_STRUCT_TYPE for SomeStruct);
#[repr(C)]
#[derive(const_field_offset::FieldOffsets)]
struct SomeStructContainer {
e: u8,
s: SomeStruct,
}
#[derive(Debug, const_field_offset::FieldOffsets, Default)]
#[repr(C)]
struct AnotherStruct {
s: String,
foo: u32,
}
impl Hello for AnotherStruct {
fn foo(&self, xx: u32) -> u32 {
self.s.len() as u32 + xx
}
fn foo_mut(&mut self, xx: u32) -> u32 {
self.foo(xx)
}
fn construct(init: u32) -> Self {
Self { s: "123".into(), foo: init }
}
fn assoc() -> isize {
999
}
fn with_lifetime(&self) -> &u32 {
&self.foo
}
}
impl HelloConsts for AnotherStruct {
const CONSTANT: usize = 99;
const SOME_OFFSET: const_field_offset::FieldOffset<AnotherStruct, u32> =
AnotherStruct::FIELD_OFFSETS.foo;
}
HelloVTable_static!(static ANOTHERSTRUCT_VTABLE for AnotherStruct);
#[test]
fn test() {
let vt = &SOME_STRUCT_TYPE;
assert_eq!(vt.assoc(), 32);
assert_eq!(vt.CONSTANT, 88);
let mut bx = vt.construct(89);
assert_eq!(bx.foo(1), 90);
assert_eq!(bx.foo_mut(6), 95);
assert_eq!(bx.foo(2), 97);
assert_eq!(bx.get_vtable().CONSTANT, 88);
let bx2 = VBox::<HelloVTable>::new(SomeStruct { e: 4, x: 23 });
assert_eq!(bx2.foo(3), 26);
assert_eq!(bx2.get_vtable().CONSTANT, 88);
assert_eq!(*bx2.SOME_OFFSET(), 23);
let mut hello = SomeStruct { e: 4, x: 44 };
{
let xref = VRef::<HelloVTable>::new(&hello);
assert_eq!(xref.foo(0), 44);
}
{
let mut xref = VRefMut::<HelloVTable>::new(&mut hello);
assert_eq!(xref.foo_mut(2), 46);
assert_eq!(*xref.SOME_OFFSET(), 46);
*xref.SOME_OFFSET_mut() = 3;
let xref2 = xref.borrow();
assert_eq!(xref2.foo(1), 4);
}
let vo = VOffset::<SomeStructContainer, HelloVTable>::new(SomeStructContainer::FIELD_OFFSETS.s);
let mut ssc = SomeStructContainer { e: 4, s: SomeStruct { e: 5, x: 32 } };
assert_eq!(vo.apply(&ssc).foo(4), 32 + 4);
assert_eq!(vo.apply_mut(&mut ssc).foo_mut(4), 32 + 4);
assert_eq!(*vo.apply(&ssc).SOME_OFFSET(), 32 + 4);
}
#[test]
fn test2() {
let mut ss = SomeStruct::construct(44);
let mut vrss = VRefMut::<HelloVTable>::new(&mut ss);
assert_eq!(vrss.downcast::<SomeStruct>().unwrap().foo_mut(4), 44 + 4);
assert!(vrss.downcast::<AnotherStruct>().is_none());
let as_ = AnotherStruct::default();
let vras = VRef::<HelloVTable>::new(&as_);
assert_eq!(vras.downcast::<AnotherStruct>().unwrap().foo(4), 4);
assert!(vras.downcast::<SomeStruct>().is_none());
}
#[test]
fn test3() {
#[vtable]
struct XxxVTable {
ret_int: fn(VRef<XxxVTable>) -> i32,
}
struct Plop(i32);
impl Xxx for Plop {
fn ret_int(&self) -> i32 {
self.0
}
}
let p = Plop(11);
new_vref!(let re : VRef<XxxVTable> for Xxx = &p);
assert_eq!(re.ret_int(), 11);
let mut p = Plop(55);
new_vref!(let mut re_mut : VRefMut<XxxVTable> for Xxx = &mut p);
assert_eq!(re_mut.ret_int(), 55);
}
#[test]
fn pin() {
#[vtable]
struct PinnedVTable {
my_func: fn(core::pin::Pin<VRef<PinnedVTable>>, u32) -> u32,
my_func2: fn(::core::pin::Pin<VRef<'_, PinnedVTable>>) -> u32,
my_func3: fn(Pin<VRefMut<PinnedVTable>>, u32) -> u32,
}
struct P(String, core::marker::PhantomPinned);
impl Pinned for P {
fn my_func(self: Pin<&Self>, p: u32) -> u32 {
self.0.len() as u32 + p
}
fn my_func2(self: Pin<&Self>) -> u32 {
self.0.len() as u32
}
fn my_func3(self: Pin<&mut Self>, _p: u32) -> u32 {
self.0.len() as u32
}
}
PinnedVTable_static!(static PVT for P);
let b = Box::pin(P("hello".to_owned(), core::marker::PhantomPinned));
let r = VRef::new_pin(b.as_ref());
assert_eq!(r.as_ref().my_func(44), 44 + 5);
assert_eq!(r.as_ref().my_func2(), 5);
}