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