1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/*!
5This crate allows you to create ffi-friendly virtual tables.
6
7## Features
8
9 - A [`#[vtable]`](macro@vtable) macro to annotate a VTable struct to generate the traits and structure
10 to safely work with it.
11 - [`VRef`]/[`VRefMut`]/[`VBox`] types. They are fat reference/box types which wrap a pointer to
12 the vtable, and a pointer to the object.
13 - [`VRc`]/[`VWeak`] types: equivalent to `std::rc::{Rc, Weak}` types but works with a vtable pointer.
14 - Ability to store constants in a vtable.
15 - These constants can even be a field offset.
16
17## Example of use:
18
19```
20use vtable::*;
21// we are going to declare a VTable structure for an Animal trait
22#[vtable]
23#[repr(C)]
24struct AnimalVTable {
25 /// pointer to a function that makes a noise. The `VRef<AnimalVTable>` is the type of
26 /// the self object.
27 ///
28 /// Note: the #[vtable] macro will automatically add `extern "C"` if that is missing.
29 make_noise: fn(VRef<AnimalVTable>, i32) -> i32,
30
31 /// if there is a 'drop' member, it is considered as the destructor.
32 drop: fn(VRefMut<AnimalVTable>),
33}
34
35struct Dog(i32);
36
37// The #[vtable] macro created the Animal Trait
38impl Animal for Dog {
39 fn make_noise(&self, intensity: i32) -> i32 {
40 println!("Wof!");
41 return self.0 * intensity;
42 }
43}
44
45// the vtable macro also exposed a macro to create a vtable
46AnimalVTable_static!(static DOG_VT for Dog);
47
48// with that, it is possible to instantiate a VBox
49let animal_box = VBox::<AnimalVTable>::new(Dog(42));
50assert_eq!(animal_box.make_noise(2), 42 * 2);
51```
52
53The [`#[vtable]`](macro@vtable) macro created the "Animal" trait.
54
55Note that the [`#[vtable]`](macro@vtable) macro is applied to the VTable struct so
56that `cbindgen` can see the actual vtable.
57
58*/
59
60#![warn(missing_docs)]
61#![no_std]
62extern crate alloc;
63
64#[doc(no_inline)]
65pub use const_field_offset::*;
66use core::marker::PhantomData;
67use core::ops::{Deref, DerefMut, Drop};
68use core::{pin::Pin, ptr::NonNull};
69#[doc(inline)]
70pub use vtable_macro::*;
71
72mod vrc;
73pub use vrc::*;
74
75/// Internal trait that is implemented by the [`#[vtable]`](macro@vtable) macro.
76///
77/// # Safety
78///
79/// The Target object needs to be implemented correctly.
80/// And there should be a `VTable::VTable::new<T>` function that returns a
81/// VTable suitable for the type T.
82pub unsafe trait VTableMeta {
83 /// That's the trait object that implements the functions
84 ///
85 /// NOTE: the size must be `2*size_of::<usize>`
86 /// and a `repr(C)` with `(vtable, ptr)` so it has the same layout as
87 /// the inner and VBox/VRef/VRefMut
88 type Target;
89
90 /// That's the VTable itself (so most likely Self)
91 type VTable: 'static;
92}
93
94/// This trait is implemented by the [`#[vtable]`](macro@vtable) macro.
95///
96/// It is implemented if the macro has a "drop" function.
97///
98/// # Safety
99/// Only the [`#[vtable]`](macro@vtable) macro should implement this trait.
100pub unsafe trait VTableMetaDrop: VTableMeta {
101 /// # Safety
102 /// `ptr` needs to be pointing to a valid allocated pointer
103 unsafe fn drop(ptr: *mut Self::Target);
104 /// allocate a new [`VBox`]
105 fn new_box<X: HasStaticVTable<Self>>(value: X) -> VBox<Self>;
106}
107
108/// Allow to associate a VTable with a type.
109///
110/// # Safety
111///
112/// The VTABLE and STATIC_VTABLE need to be a valid virtual table
113/// corresponding to pointer to Self instance.
114pub unsafe trait HasStaticVTable<VT>
115where
116 VT: ?Sized + VTableMeta,
117{
118 /// Safety: must be a valid VTable for Self
119 fn static_vtable() -> &'static VT::VTable;
120}
121
122#[derive(Copy, Clone)]
123/// The inner structure of VRef, VRefMut, and VBox.
124///
125/// Invariant: _vtable and _ptr are valid pointer for the lifetime of the container.
126/// _ptr is an instance of the object represented by _vtable
127#[allow(dead_code)]
128#[repr(C)]
129struct Inner {
130 vtable: NonNull<u8>,
131 ptr: NonNull<u8>,
132}
133
134impl Inner {
135 /// Transmute a reference to self into a reference to T::Target.
136 fn deref<T: ?Sized + VTableMeta>(&self) -> *const T::Target {
137 debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>());
138 self as *const Inner as *const T::Target
139 }
140
141 /// Same as [`Self::deref`].
142 fn deref_mut<T: ?Sized + VTableMeta>(&mut self) -> *mut T::Target {
143 debug_assert_eq!(core::mem::size_of::<T::Target>(), core::mem::size_of::<Inner>());
144 self as *mut Inner as *mut T::Target
145 }
146}
147
148/// An equivalent of a Box that holds a pointer to a VTable and a pointer to an instance.
149/// A VBox frees the instance when dropped.
150///
151/// The type parameter is supposed to be the VTable type.
152///
153/// The VBox implements Deref so one can access all the members of the vtable.
154///
155/// This is only valid if the VTable has a `drop` function (so that the [`#[vtable]`](macro@vtable) macro
156/// implements the `VTableMetaDrop` trait for it)
157#[repr(transparent)]
158pub struct VBox<T: ?Sized + VTableMetaDrop> {
159 inner: Inner,
160 phantom: PhantomData<T::Target>,
161}
162
163impl<T: ?Sized + VTableMetaDrop> Deref for VBox<T> {
164 type Target = T::Target;
165 fn deref(&self) -> &Self::Target {
166 unsafe { &*self.inner.deref::<T>() }
167 }
168}
169impl<T: ?Sized + VTableMetaDrop> DerefMut for VBox<T> {
170 fn deref_mut(&mut self) -> &mut Self::Target {
171 unsafe { &mut *(self.inner.deref_mut::<T>() as *mut _) }
172 }
173}
174
175impl<T: ?Sized + VTableMetaDrop> Drop for VBox<T> {
176 fn drop(&mut self) {
177 unsafe {
178 T::drop(self.inner.deref::<T>() as *mut _);
179 }
180 }
181}
182
183impl<T: ?Sized + VTableMetaDrop> VBox<T> {
184 /// Create a new VBox from an instance of a type that can be associated with a VTable.
185 ///
186 /// Will move the instance on the heap.
187 ///
188 /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by
189 /// the #[vtable] macro)
190 pub fn new<X: HasStaticVTable<T>>(value: X) -> Self {
191 T::new_box(value)
192 }
193
194 /// Create a new VBox from raw pointers
195 /// # Safety
196 /// The `ptr` needs to be a valid object fitting the `vtable`.
197 /// ptr must be properly allocated so it can be dropped.
198 pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
199 Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData }
200 }
201
202 /// Gets a VRef pointing to this box
203 pub fn borrow(&self) -> VRef<'_, T> {
204 unsafe { VRef::from_inner(self.inner) }
205 }
206
207 /// Gets a VRefMut pointing to this box
208 pub fn borrow_mut(&mut self) -> VRefMut<'_, T> {
209 unsafe { VRefMut::from_inner(self.inner) }
210 }
211
212 /// Leaks the content of the box.
213 pub fn leak(self) -> VRefMut<'static, T> {
214 let inner = self.inner;
215 core::mem::forget(self);
216 unsafe { VRefMut::from_inner(inner) }
217 }
218}
219
220/// `VRef<'a MyTraitVTable>` can be thought as a `&'a dyn MyTrait`
221///
222/// It will dereference to a structure that has the same members as MyTrait.
223#[repr(transparent)]
224pub struct VRef<'a, T: ?Sized + VTableMeta> {
225 inner: Inner,
226 phantom: PhantomData<&'a T::Target>,
227}
228
229// Need to implement manually otherwise it is not implemented if T does not implement Copy / Clone
230impl<'a, T: ?Sized + VTableMeta> Copy for VRef<'a, T> {}
231
232impl<'a, T: ?Sized + VTableMeta> Clone for VRef<'a, T> {
233 fn clone(&self) -> Self {
234 *self
235 }
236}
237
238impl<'a, T: ?Sized + VTableMeta> Deref for VRef<'a, T> {
239 type Target = T::Target;
240 fn deref(&self) -> &Self::Target {
241 unsafe { &*self.inner.deref::<T>() }
242 }
243}
244
245impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> {
246 /// Create a new VRef from an reference of a type that can be associated with a VTable.
247 ///
248 /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by
249 /// the #[vtable] macro)
250 pub fn new<X: HasStaticVTable<T>>(value: &'a X) -> Self {
251 Self {
252 inner: Inner {
253 vtable: NonNull::from(X::static_vtable()).cast(),
254 ptr: NonNull::from(value).cast(),
255 },
256 phantom: PhantomData,
257 }
258 }
259
260 /// Create a new Pin<VRef<_>> from a pinned reference. This is similar to `VRef::new`.
261 pub fn new_pin<X: HasStaticVTable<T>>(value: core::pin::Pin<&'a X>) -> Pin<Self> {
262 // Since Value is pinned, this means it is safe to construct a Pin
263 unsafe {
264 Pin::new_unchecked(Self {
265 inner: Inner {
266 vtable: NonNull::from(X::static_vtable()).cast(),
267 ptr: NonNull::from(value.get_ref()).cast(),
268 },
269 phantom: PhantomData,
270 })
271 }
272 }
273
274 unsafe fn from_inner(inner: Inner) -> Self {
275 Self { inner, phantom: PhantomData }
276 }
277
278 /// Create a new VRef from raw pointers
279 /// # Safety
280 /// The `ptr` needs to be a valid object fitting the `vtable`.
281 /// Both vtable and ptr lifetime must outlive 'a
282 pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
283 Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData }
284 }
285
286 /// Return a reference of the given type if the type is matching.
287 pub fn downcast<X: HasStaticVTable<T>>(&self) -> Option<&X> {
288 if self.inner.vtable == NonNull::from(X::static_vtable()).cast() {
289 // Safety: We just checked that the vtable fits
290 unsafe { Some(self.inner.ptr.cast().as_ref()) }
291 } else {
292 None
293 }
294 }
295
296 /// Return a reference of the given type if the type is matching
297 pub fn downcast_pin<X: HasStaticVTable<T>>(this: Pin<Self>) -> Option<Pin<&'a X>> {
298 let inner = unsafe { Pin::into_inner_unchecked(this).inner };
299 if inner.vtable == NonNull::from(X::static_vtable()).cast() {
300 // Safety: We just checked that the vtable fits
301 unsafe { Some(Pin::new_unchecked(inner.ptr.cast().as_ref())) }
302 } else {
303 None
304 }
305 }
306
307 /// Returns a pointer to the VRef's instance. This is primarily useful for comparisons.
308 pub fn as_ptr(this: Self) -> NonNull<u8> {
309 this.inner.ptr
310 }
311}
312
313/// `VRefMut<'a MyTraitVTable>` can be thought as a `&'a mut dyn MyTrait`
314///
315/// It will dereference to a structure that has the same members as MyTrait.
316#[repr(transparent)]
317pub struct VRefMut<'a, T: ?Sized + VTableMeta> {
318 inner: Inner,
319 phantom: PhantomData<&'a mut T::Target>,
320}
321
322impl<'a, T: ?Sized + VTableMeta> Deref for VRefMut<'a, T> {
323 type Target = T::Target;
324 fn deref(&self) -> &Self::Target {
325 unsafe { &*self.inner.deref::<T>() }
326 }
327}
328
329impl<'a, T: ?Sized + VTableMeta> DerefMut for VRefMut<'a, T> {
330 fn deref_mut(&mut self) -> &mut Self::Target {
331 unsafe { &mut *(self.inner.deref_mut::<T>() as *mut _) }
332 }
333}
334
335impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> {
336 /// Create a new VRef from a mutable reference of a type that can be associated with a VTable.
337 ///
338 /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by
339 /// the #[vtable] macro)
340 pub fn new<X: HasStaticVTable<T>>(value: &'a mut X) -> Self {
341 Self {
342 inner: Inner {
343 vtable: NonNull::from(X::static_vtable()).cast(),
344 ptr: NonNull::from(value).cast(),
345 },
346 phantom: PhantomData,
347 }
348 }
349
350 unsafe fn from_inner(inner: Inner) -> Self {
351 Self { inner, phantom: PhantomData }
352 }
353
354 /// Create a new VRefMut from raw pointers
355 /// # Safety
356 /// The `ptr` needs to be a valid object fitting the `vtable`.
357 /// Both vtable and ptr lifetime must outlive 'a.
358 /// Can create mutable reference to ptr, so no other code can create mutable reference of ptr
359 /// during the life time 'a.
360 pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
361 Self { inner: Inner { vtable: vtable.cast(), ptr }, phantom: PhantomData }
362 }
363
364 /// Borrow this to obtain a VRef.
365 pub fn borrow(&self) -> VRef<'_, T> {
366 unsafe { VRef::from_inner(self.inner) }
367 }
368
369 /// Borrow this to obtain a new VRefMut.
370 pub fn borrow_mut(&mut self) -> VRefMut<'_, T> {
371 unsafe { VRefMut::from_inner(self.inner) }
372 }
373
374 /// Create a VRef with the same lifetime as the original lifetime.
375 pub fn into_ref(self) -> VRef<'a, T> {
376 unsafe { VRef::from_inner(self.inner) }
377 }
378
379 /// Return a reference of the given type if the type is matching.
380 pub fn downcast<X: HasStaticVTable<T>>(&mut self) -> Option<&mut X> {
381 if self.inner.vtable == NonNull::from(X::static_vtable()).cast() {
382 // Safety: We just checked that the vtable fits
383 unsafe { Some(self.inner.ptr.cast().as_mut()) }
384 } else {
385 None
386 }
387 }
388}
389
390/** Creates a [`VRef`] or a [`VRefMut`] suitable for an instance that implements the trait
391
392When possible, [`VRef::new`] or [`VRefMut::new`] should be preferred, as they use a static vtable.
393But when using the generated `XxxVTable_static!` macro that is not possible and this macro can be
394used instead.
395Note that the `downcast` will not work with references created with this macro.
396
397```
398use vtable::*;
399#[vtable]
400struct MyVTable { /* ... */ }
401struct Something { /* ... */};
402impl My for Something {};
403
404let mut s = Something { /* ... */};
405// declare a my_vref variable for the said VTable
406new_vref!(let my_vref : VRef<MyVTable> for My = &s);
407
408// same but mutable
409new_vref!(let mut my_vref_m : VRefMut<MyVTable> for My = &mut s);
410
411```
412*/
413#[macro_export]
414macro_rules! new_vref {
415 (let $ident:ident : VRef<$vtable:ty> for $trait_:path = $e:expr) => {
416 // ensure that the type of the expression is correct
417 let vtable = {
418 use $crate::VTableMeta;
419 fn get_vt<X: $trait_>(_: &X) -> <$vtable as VTableMeta>::VTable {
420 <$vtable as VTableMeta>::VTable::new::<X>()
421 }
422 get_vt($e)
423 };
424
425 let $ident = {
426 use $crate::VTableMeta;
427 fn create<'a, X: $trait_>(
428 vtable: &'a <$vtable as VTableMeta>::VTable,
429 val: &'a X,
430 ) -> $crate::VRef<'a, <$vtable as VTableMeta>::VTable> {
431 use ::core::ptr::NonNull;
432 // Safety: we constructed the vtable such that it fits for the value
433 unsafe { $crate::VRef::from_raw(NonNull::from(vtable), NonNull::from(val).cast()) }
434 }
435 create(&vtable, $e)
436 };
437 };
438 (let mut $ident:ident : VRefMut<$vtable:ty> for $trait_:path = $e:expr) => {
439 // ensure that the type of the expression is correct
440 let vtable = {
441 use $crate::VTableMeta;
442 fn get_vt<X: $trait_>(_: &mut X) -> <$vtable as VTableMeta>::VTable {
443 <$vtable as VTableMeta>::VTable::new::<X>()
444 }
445 get_vt($e)
446 };
447
448 let mut $ident = {
449 use $crate::VTableMeta;
450 fn create<'a, X: $trait_>(
451 vtable: &'a <$vtable as VTableMeta>::VTable,
452 val: &'a mut X,
453 ) -> $crate::VRefMut<'a, <$vtable as VTableMeta>::VTable> {
454 use ::core::ptr::NonNull;
455 // Safety: we constructed the vtable such that it fits for the value
456 unsafe {
457 $crate::VRefMut::from_raw(NonNull::from(vtable), NonNull::from(val).cast())
458 }
459 }
460 create(&vtable, $e)
461 };
462 };
463}
464
465/// Represents an offset to a field of type matching the vtable, within the Base container structure.
466#[repr(C)]
467pub struct VOffset<Base, T: ?Sized + VTableMeta, PinFlag = NotPinned> {
468 vtable: &'static T::VTable,
469 /// Safety invariant: the vtable is valid, and the field at the given offset within Base is
470 /// matching with the vtable
471 offset: usize,
472 phantom: PhantomData<FieldOffset<Base, (), PinFlag>>,
473}
474
475impl<Base, T: ?Sized + VTableMeta, PinFlag> core::fmt::Debug for VOffset<Base, T, PinFlag> {
476 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
477 write!(f, "VOffset({})", self.offset)
478 }
479}
480
481impl<Base, T: ?Sized + VTableMeta, Flag> VOffset<Base, T, Flag> {
482 /// Apply this offset to a reference to the base to obtain a [`VRef`] with the same
483 /// lifetime as the base lifetime
484 #[inline]
485 pub fn apply(self, base: &Base) -> VRef<'_, T> {
486 let ptr = base as *const Base as *const u8;
487 unsafe {
488 VRef::from_raw(
489 NonNull::from(self.vtable),
490 NonNull::new_unchecked(ptr.add(self.offset) as *mut _),
491 )
492 }
493 }
494
495 /// Apply this offset to a reference to the base to obtain a [`VRefMut`] with the same
496 /// lifetime as the base lifetime
497 #[inline]
498 pub fn apply_mut(self, base: &mut Base) -> VRefMut<'_, T> {
499 let ptr = base as *mut Base as *mut u8;
500 unsafe {
501 VRefMut::from_raw(
502 NonNull::from(self.vtable),
503 NonNull::new_unchecked(ptr.add(self.offset)),
504 )
505 }
506 }
507
508 /// Create an new VOffset from a [`FieldOffset`] where the target type implement the
509 /// [`HasStaticVTable`] trait.
510 #[inline]
511 pub fn new<X: HasStaticVTable<T>>(o: FieldOffset<Base, X, Flag>) -> Self {
512 Self { vtable: X::static_vtable(), offset: o.get_byte_offset(), phantom: PhantomData }
513 }
514
515 /// Create a new VOffset from raw data
516 ///
517 /// # Safety
518 /// There must be a field that matches the vtable at offset T in base.
519 #[inline]
520 pub unsafe fn from_raw(vtable: &'static T::VTable, offset: usize) -> Self {
521 Self { vtable, offset, phantom: PhantomData }
522 }
523}
524
525impl<Base, T: ?Sized + VTableMeta> VOffset<Base, T, AllowPin> {
526 /// Apply this offset to a reference to the base to obtain a `Pin<VRef<'a, T>>` with the same
527 /// lifetime as the base lifetime
528 #[inline]
529 pub fn apply_pin(self, base: Pin<&Base>) -> Pin<VRef<T>> {
530 let ptr: *mut u8 = base.get_ref() as *const Base as *mut u8;
531 unsafe {
532 Pin::new_unchecked(pointer:VRef::from_raw(
533 vtable:NonNull::from(self.vtable),
534 ptr:NonNull::new_unchecked(ptr:ptr.add(self.offset)),
535 ))
536 }
537 }
538}
539
540// Need to implement manually otherwise it is not implemented if T does not implement Copy / Clone
541impl<Base, T: ?Sized + VTableMeta, Flag> Copy for VOffset<Base, T, Flag> {}
542
543impl<Base, T: ?Sized + VTableMeta, Flag> Clone for VOffset<Base, T, Flag> {
544 fn clone(&self) -> Self {
545 *self
546 }
547}
548
549#[cfg(doctest)]
550mod compile_fail_tests;
551
552/// re-export for the macro
553#[doc(hidden)]
554pub mod internal {
555 pub use alloc::alloc::dealloc;
556 pub use alloc::boxed::Box;
557}
558