Skip to main content

slint_interpreter/
dynamic_type.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4// cSpell: ignore autorefs
5/*!
6 This module create dynamic types
7
8 The main entry point for this module is the TypeBuilder
9*/
10
11use core::alloc::Layout;
12use generativity::Id;
13use i_slint_core::rtti::FieldOffset;
14use std::rc::Rc;
15
16unsafe fn construct_fn<T: Default>(ptr: *mut u8) {
17    unsafe { core::ptr::write(ptr as *mut T, T::default()) };
18}
19unsafe fn drop_fn<T>(ptr: *mut u8) {
20    unsafe { core::ptr::drop_in_place(ptr as *mut T) };
21}
22
23/// Information for type that can be added to a dynamic type.
24///
25/// Let the builder know how to construct and build these fields
26#[derive(Copy, Clone)]
27pub struct StaticTypeInfo {
28    /// Invariant: this function must be safe to call on a uninitialized memory matching `mem_layout`.
29    /// Can only be None if the field is meant to be initialized by another mean (e.g, the type pointer
30    /// allocated at the beginning of the type)
31    construct: Option<unsafe fn(*mut u8)>,
32    /// Invariant: this function must be safe to call on an instance created by the `construct` function.
33    /// If None, the type does not need drop.
34    drop: Option<unsafe fn(*mut u8)>,
35    /// Memory layout of the type
36    mem_layout: Layout,
37}
38
39impl StaticTypeInfo {
40    /// Returns a StaticTypeInfo suitable for the type `T`
41    pub fn new<T: Default>() -> StaticTypeInfo {
42        let drop = if core::mem::needs_drop::<T>() { Some(drop_fn::<T> as _) } else { None };
43        StaticTypeInfo { construct: Some(construct_fn::<T>), drop, mem_layout: Layout::new::<T>() }
44    }
45}
46
47/// Internal structure representing a field within a dynamic type
48struct FieldInfo {
49    construct: Option<unsafe fn(*mut u8)>,
50    drop: Option<unsafe fn(*mut u8)>,
51    offset: usize,
52}
53
54/// A TypeInfo represents the metadata required to create and drop dynamic type
55///
56/// It needs to be built with the TypeBuilder.
57pub struct TypeInfo<'id> {
58    mem_layout: core::alloc::Layout,
59    /// Invariant: each field must represent a valid field within the `mem_layout`
60    /// and the construct and drop function must be valid so that each field can
61    /// be constructed and dropped correctly.
62    /// The first FieldInfo must be related to the `Rc<TypeInfo>` member at the beginning
63    fields: Vec<FieldInfo>,
64
65    #[allow(unused)]
66    id: Id<'id>,
67}
68
69/// A builder for a dynamic type.
70///
71/// Call `add_field()` for each type, and then `build()` to return a TypeInfo
72pub struct TypeBuilder<'id> {
73    /// max alignment in byte of the types
74    align: usize,
75    /// Size in byte of the type so far (not including the trailing padding)
76    size: usize,
77    fields: Vec<FieldInfo>,
78    id: Id<'id>,
79}
80
81impl<'id> TypeBuilder<'id> {
82    pub fn new(id: generativity::Guard<'id>) -> Self {
83        let mut s = Self { align: 1, size: 0, fields: Vec::new(), id: id.into() };
84        type T<'id> = Rc<TypeInfo<'id>>;
85        s.add_field(StaticTypeInfo {
86            construct: None,
87            drop: Some(drop_fn::<T<'id>>),
88            mem_layout: Layout::new::<T<'id>>(),
89        });
90        s
91    }
92
93    /// Convenience to call add_field with the StaticTypeInfo for a field
94    pub fn add_field_type<T: Default>(&mut self) -> FieldOffset<Instance<'id>, T> {
95        unsafe { FieldOffset::new_from_offset_pinned(self.add_field(StaticTypeInfo::new::<T>())) }
96    }
97
98    /// Add a field in this dynamic type.
99    ///
100    /// Returns the offset, in bytes, of the added field in within the dynamic type.
101    /// This takes care of alignment of the types.
102    pub fn add_field(&mut self, ty: StaticTypeInfo) -> usize {
103        let align = ty.mem_layout.align();
104        let len_rounded_up = self.size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
105
106        self.fields.push(FieldInfo {
107            construct: ty.construct,
108            drop: ty.drop,
109            offset: len_rounded_up,
110        });
111        self.size = len_rounded_up + ty.mem_layout.size();
112        self.align = self.align.max(align);
113        len_rounded_up
114    }
115
116    pub fn build(self) -> Rc<TypeInfo<'id>> {
117        let size = self.size.wrapping_add(self.align).wrapping_sub(1) & !self.align.wrapping_sub(1);
118        Rc::new(TypeInfo {
119            mem_layout: core::alloc::Layout::from_size_align(size, self.align).unwrap(),
120            fields: self.fields,
121            id: self.id,
122        })
123    }
124}
125
126impl<'id> TypeInfo<'id> {
127    /// Create an instance of this type.
128    ///
129    /// The instance will be allocated on the heap.
130    /// The instance must be freed with `delete_instance`
131    pub fn create_instance(self: Rc<Self>) -> InstanceBox<'id> {
132        unsafe {
133            let mem = std::alloc::alloc(self.mem_layout) as *mut Instance;
134            self.create_instance_in_place(mem);
135            InstanceBox(core::ptr::NonNull::new_unchecked(mem))
136        }
137    }
138
139    /// Create an instance of this type.
140    ///
141    /// Safety: The memory must point to a region large enough to fit [`Self::layout()`]
142    /// that can safely be overwritten
143    pub unsafe fn create_instance_in_place(self: Rc<Self>, mem: *mut Instance<'id>) {
144        // Safety: the TypeInfo invariant means that the constructor can be called
145        let mem = mem as *mut u8;
146        unsafe { std::ptr::write(mem as *mut Rc<_>, self.clone()) };
147        for f in &self.fields {
148            if let Some(ctor) = f.construct {
149                unsafe { ctor(mem.add(f.offset)) };
150            }
151        }
152    }
153
154    /// Drop and free the memory of this instance
155    ///
156    /// Safety, the instance must have been created by `TypeInfo::create_instance_in_place`
157    pub unsafe fn drop_in_place(instance: *mut Instance) {
158        let type_info = unsafe { (*instance).type_info.clone() };
159        let mem = instance as *mut u8;
160        for f in &type_info.fields {
161            if let Some(dtor) = f.drop {
162                unsafe { dtor(mem.add(f.offset)) };
163            }
164        }
165    }
166
167    /// Drop and free the memory of this instance
168    ///
169    /// Safety, the instance must have been created by `TypeInfo::create_instance`
170    unsafe fn delete_instance(instance: *mut Instance) {
171        unsafe {
172            // removing the & causes a dangerous_implicit_autorefs error
173            #[allow(clippy::needless_borrow)]
174            let mem_layout = (&(*instance).type_info).mem_layout;
175            Self::drop_in_place(instance);
176            let mem = instance as *mut u8;
177            std::alloc::dealloc(mem, mem_layout);
178        }
179    }
180
181    pub fn layout(&self) -> core::alloc::Layout {
182        self.mem_layout
183    }
184}
185
186/// Opaque type that represents something created with `TypeInfo::create_instance`
187#[repr(C)]
188pub struct Instance<'id> {
189    type_info: Rc<TypeInfo<'id>>,
190    _opaque: [u8; 0],
191}
192
193impl<'id> Instance<'id> {
194    /// return the TypeInfo which build this instance
195    pub fn type_info(&self) -> Rc<TypeInfo<'id>> {
196        self.type_info.clone()
197    }
198}
199
200impl core::fmt::Debug for Instance<'_> {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        write!(f, "Instance({self:p})")
203    }
204}
205
206/// A pointer to an Instance that automatically frees the memory after use
207pub struct InstanceBox<'id>(core::ptr::NonNull<Instance<'id>>);
208
209impl<'id> InstanceBox<'id> {
210    /// return a pointer to the instance
211    pub fn as_ptr(&self) -> core::ptr::NonNull<Instance<'id>> {
212        self.0
213    }
214
215    pub fn as_pin_ref(&self) -> core::pin::Pin<&Instance<'id>> {
216        unsafe { core::pin::Pin::new_unchecked(self.0.as_ref()) }
217    }
218
219    pub fn as_mut(&mut self) -> &mut Instance<'id> {
220        unsafe { self.0.as_mut() }
221    }
222}
223
224impl Drop for InstanceBox<'_> {
225    fn drop(&mut self) {
226        unsafe { TypeInfo::delete_instance(self.0.as_mut()) }
227    }
228}