1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial

/*!
 This module create dynamic types

 The main entry point for this module is the TypeBuilder
*/

use core::alloc::Layout;
use generativity::Id;
use i_slint_core::rtti::FieldOffset;
use std::rc::Rc;

unsafe fn construct_fn<T: Default>(ptr: *mut u8) {
    core::ptr::write(ptr as *mut T, T::default());
}
unsafe fn drop_fn<T>(ptr: *mut u8) {
    core::ptr::drop_in_place(ptr as *mut T);
}

/// Information for type that can be added to a dynamic type.
///
/// Let the builder know how to construct and build these fields
#[derive(Copy, Clone)]
pub struct StaticTypeInfo {
    /// Invariant: this function must be safe to call on a uninitialized memory matching `mem_layout`.
    /// Can only be None if the field is meant to be initialized by another mean (e.g, the type pointer
    /// allocated at the beginning of the type)
    construct: Option<unsafe fn(*mut u8)>,
    /// Invariant: this function must be safe to call on an instance created by the `construct` function.
    /// If None, the type does not need drop.
    drop: Option<unsafe fn(*mut u8)>,
    /// Memory layout of the type
    mem_layout: Layout,
}

impl StaticTypeInfo {
    /// Returns a StaticTypeInfo suitable for the type `T`
    pub fn new<T: Default>() -> StaticTypeInfo {
        let drop = if core::mem::needs_drop::<T>() { Some(drop_fn::<T> as _) } else { None };
        StaticTypeInfo { construct: Some(construct_fn::<T>), drop, mem_layout: Layout::new::<T>() }
    }
}

/// Internal structure representing a field within a dynamic type
struct FieldInfo {
    construct: Option<unsafe fn(*mut u8)>,
    drop: Option<unsafe fn(*mut u8)>,
    offset: usize,
}

/// A TypeInfo represents the metadata required to create and drop dynamic type
///
/// It needs to be built with the TypeBuilder.
pub struct TypeInfo<'id> {
    mem_layout: core::alloc::Layout,
    /// Invariant: each field must represent a valid field within the `mem_layout`
    /// and the construct and drop function must be valid so that each field can
    /// be constructed and dropped correctly.
    /// The first FieldInfo must be related to the `Rc<TypeInfo>` member at the beginning
    fields: Vec<FieldInfo>,

    #[allow(unused)]
    id: Id<'id>,
}

/// A builder for a dynamic type.
///
/// Call `add_field()` for each type, and then `build()` to return a TypeInfo
pub struct TypeBuilder<'id> {
    /// max alignment in byte of the types
    align: usize,
    /// Size in byte of the type so far (not including the trailing padding)
    size: usize,
    fields: Vec<FieldInfo>,
    id: Id<'id>,
}

impl<'id> TypeBuilder<'id> {
    pub fn new(id: generativity::Guard<'id>) -> Self {
        let mut s = Self { align: 1, size: 0, fields: vec![], id: id.into() };
        type T<'id> = Rc<TypeInfo<'id>>;
        s.add_field(StaticTypeInfo {
            construct: None,
            drop: Some(drop_fn::<T<'id>>),
            mem_layout: Layout::new::<T<'id>>(),
        });
        s
    }

    /// Convenience to call add_field with the StaticTypeInfo for a field
    pub fn add_field_type<T: Default>(&mut self) -> FieldOffset<Instance<'id>, T> {
        unsafe { FieldOffset::new_from_offset_pinned(self.add_field(StaticTypeInfo::new::<T>())) }
    }

    /// Add a field in this dynamic type.
    ///
    /// Returns the offset, in bytes, of the added field in within the dynamic type.
    /// This takes care of alignment of the types.
    pub fn add_field(&mut self, ty: StaticTypeInfo) -> usize {
        let align = ty.mem_layout.align();
        let len_rounded_up = self.size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);

        self.fields.push(FieldInfo {
            construct: ty.construct,
            drop: ty.drop,
            offset: len_rounded_up,
        });
        self.size = len_rounded_up + ty.mem_layout.size();
        self.align = self.align.max(align);
        len_rounded_up
    }

    pub fn build(self) -> Rc<TypeInfo<'id>> {
        let size = self.size.wrapping_add(self.align).wrapping_sub(1) & !self.align.wrapping_sub(1);
        Rc::new(TypeInfo {
            mem_layout: core::alloc::Layout::from_size_align(size, self.align).unwrap(),
            fields: self.fields,
            id: self.id,
        })
    }
}

impl<'id> TypeInfo<'id> {
    /// Create an instance of this type.
    ///
    /// The instance will be allocated on the heap.
    /// The instance must be freed with `delete_instance`
    pub fn create_instance(self: Rc<Self>) -> InstanceBox<'id> {
        unsafe {
            let mem = std::alloc::alloc(self.mem_layout) as *mut Instance;
            self.create_instance_in_place(mem);
            InstanceBox(core::ptr::NonNull::new_unchecked(mem))
        }
    }

    /// Create an instance of this type.
    ///
    /// Safety: The memory must point to a region large enough to fit [`Self::layout()`]
    /// that can safely be overwritten
    pub unsafe fn create_instance_in_place(self: Rc<Self>, mem: *mut Instance<'id>) {
        // Safety: the TypeInfo invariant means that the constructor can be called
        let mem = mem as *mut u8;
        std::ptr::write(mem as *mut Rc<_>, self.clone());
        for f in &self.fields {
            if let Some(ctor) = f.construct {
                ctor(mem.add(f.offset));
            }
        }
    }

    /// Drop and free the memory of this instance
    ///
    /// Safety, the instance must have been created by `TypeInfo::create_instance_in_place`
    pub unsafe fn drop_in_place(instance: *mut Instance) {
        let type_info = (*instance).type_info.clone();
        let mem = instance as *mut u8;
        for f in &type_info.fields {
            if let Some(dtor) = f.drop {
                dtor(mem.add(f.offset));
            }
        }
    }

    /// Drop and free the memory of this instance
    ///
    /// Safety, the instance must have been created by `TypeInfo::create_instance`
    unsafe fn delete_instance(instance: *mut Instance) {
        let mem_layout = (*instance).type_info.mem_layout;
        Self::drop_in_place(instance);
        let mem = instance as *mut u8;
        std::alloc::dealloc(mem, mem_layout);
    }

    pub fn layout(&self) -> core::alloc::Layout {
        self.mem_layout
    }
}

/// Opaque type that represents something created with `TypeInfo::create_instance`
#[repr(C)]
pub struct Instance<'id> {
    type_info: Rc<TypeInfo<'id>>,
    _opaque: [u8; 0],
}

impl<'id> Instance<'id> {
    /// return the TypeInfo which build this instance
    pub fn type_info(&self) -> Rc<TypeInfo<'id>> {
        self.type_info.clone()
    }
}

impl<'id> core::fmt::Debug for Instance<'id> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Instance({:p})", self)
    }
}

/// A pointer to an Instance that automatically frees the memory after use
pub struct InstanceBox<'id>(core::ptr::NonNull<Instance<'id>>);

impl<'id> InstanceBox<'id> {
    /// return a pointer to the instance
    pub fn as_ptr(&self) -> core::ptr::NonNull<Instance<'id>> {
        self.0
    }

    pub fn as_pin_ref(&self) -> core::pin::Pin<&Instance<'id>> {
        unsafe { core::pin::Pin::new_unchecked(self.0.as_ref()) }
    }

    pub fn as_mut(&mut self) -> &mut Instance<'id> {
        unsafe { self.0.as_mut() }
    }
}

impl<'id> Drop for InstanceBox<'id> {
    fn drop(&mut self) {
        unsafe { TypeInfo::delete_instance(self.0.as_mut()) }
    }
}